避免获取和设置方法,显示用户信息


10

背景

我正在阅读“清洁代码手册”,并且在并行中,我正在像银行账户那样从事诸如体操之类的身体对象卡塔(Kata)的研究:

健美操对象的第9条规则是我们不要使用吸气剂或吸气剂。

看起来很有趣,我同意这一原则。而且,在“清洁代码”的第98-99页,作者解释说,getters / setters破坏了抽象,并且我们不必询问对象,而必须告诉对象。

在我看来,这是完全合理的,我完全同意这一原则。问题出在实践中。

语境

例如,我有一个必须列出一些用户并显示用户详细信息的应用程序。

我的用户包括:

-> Name
   --> Firstname --> String
   --> Lastname --> String
-> PostalAddress
   --> Street --> String
   --> PostalCode --> String

问题

当我只需要显示一个简单的信息(并且我必须确认我不需要对该特定字段进行额外的操作)时,我该怎么办?或者如何避免吸气剂以简单的方式显示Firstname值(随机)输出支持?

我想到什么

一种解决方案是:

user.getName().getFirstName().getStringValue()

这非常可怕,违反了许多健美操规则,并违反了Demeter法则。

另一个可能是这样的:

String firstName = user.provideFirstnameForOutput();
// That would have called in the user object =>
String firstName = name.provideFirstnameForOutput();
// That would have called in the name object =>
String firstName = firstname.provideFirstnameForOutput();

但是我对这种解决方案感到不满意,因为它似乎只是一种“高阶访问器”,就像使用旨在匹配Demeter法则的方法绕过标准的getter / setter方法一样。

任何的想法 ?

Answers:


17

关于避免getter和setter的想法的普遍误解是到处都避免使用getter和setter,这在您与用户进行交互的体系结构表面时是不可能的。

您应该避免在应用程序的业务逻辑部分中使用getter和setter方法,在这种情况下,应使用提供上下文的聚合来保护对象,而方法应充当命令。

为避免使用getter和setter,您可以设计一个类似于响应式编程的解决方案,通过可观察的实体来实现报告系统,但这会使体系结构极为复杂,即使您的应用程序确实对用户界面非常有用,但它在应用程序的CRUD级别上毫无意义。

是否应该考虑使用getter / setter完全取决于您当前正在处理的应用程序部分。如果您关心的是使用getter的用户界面是完全可以的,那么对于业务逻辑而言,您将基于通过getter检索到的值来运行部分代码,而不是那么多(这表明该逻辑实际上应该封装在其中)调用getter的类)。

而且,demeter的定律不是关于点的计数,而仅仅是关于通过使用类中的getter来获取上下文的类,以获取其自身不应该访问的组件。

如果您觉得用户界面在业务模型中过于深入,可以考虑将视图模型引入系统,负责将模型的特定部分转换为可视化表示形式。


好的,我想到了这个建议,就像我的域实体之间的映射器,到我的表示层的自定义DTO一样,它会有一些访问器吗?
mfrachet

@Margin差不多,是的。
安迪

感谢您的回答,所有这些层叠的声音听起来都很好而且很流畅;)
mfrachet

2

可以考虑的一个方向是提供通用的字符串格式化成员函数,而不是提供对原始数据的访问。像这样:

String lastName = user.formatDescription("$(Lastname)");
String fullName = user.formatDescription("$(Lastname), $(Firstname)");
String abbreviated = user.formatDescription("$(FnAbb). $(LnAbb).");

您会看到,使用这种方法,您不仅限于按原样提供完整数据,还可以提供以方便,有意义的方式转换数据的方法。例如,您可能需要使用字符大小写技巧:

String accountName = user.formatDescription("$(firstname).$(lastname)");

您还可以一次定义一些常用的,可能是复杂的格式,例如,您可以说

String fullAddress = user.formatDescription(User.kAddressFormat);

这种方法的优点在于,它将各个字符串保留在User类的内部,同时实际上为调用代码提供了更多的功能。但是,不利的一面是,您需要实现模板机制,formatDescription()其中将包含几行代码。

因此,这可能完全是矫kill过正:永远不要忘记编程原则只是指导原则。每当遵循另一条原则违反KISS原则时,最好以简单的方式做到这一点。因此,除非您至少有某种需要这样的格式化成员的权限,否则我不会为了简单起见而使用基于访问器的方法来实现它。


5
但是,对我而言,这似乎违反了SRP。User解析和编译/解释模板语言真的是对象的责任吗?
约尔格W¯¯米塔格

@JörgWMittag不一定。就像我说的那样,KISS原则优先。而且我没有说过User该类必须自己实现此功能。如果我只有两个需要这种功能的类,则可以押注我将模板替换机制分解为自己的类。这旨在作为示例说明如何将User提供的抽象提升到实际上不仅仅是数据容器的程度。而且我相信,这就是避免访问器的全部目的:进行OOP而不是处理一堆structs。
cmaster-恢复莫妮卡

您知道KISS完全是主观的且SRP是客观的吗?KISS不会告诉您有关做什么的任何事情。对您来说,“简单”对我而言可能不是“简单”。如果与KISS或SRP争论,我会看到质量上的真正差异。
oopexpert
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.