使用getter和setter(只获取和设置)而不是简单地为这些变量使用公共字段有什么好处?

如果getter和setter所做的不仅仅是简单的get/set,我可以很快地解决这个问题,但我不是100%清楚如何做到:

public String foo;

比:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

而前者需要的样板代码要少得多。


当前回答

谢谢,这真的澄清了我的想法。现在有10个(几乎)不使用getter和setter的好理由:

当您意识到您需要做的不仅仅是设置和获取值时,您可以将字段设为私有,这会立即告诉您直接访问它的位置。在那里执行的任何验证都只能是上下文无关的,而实际上很少进行这种验证。您可以更改设置的值-当调用者传递给您一个他们(震惊恐惧)希望您按原样存储的值时,这绝对是一场噩梦。你可以隐藏内部表示-太棒了,所以你要确保所有这些操作都是对称的,对吗?你已经将你的公共界面与表单下的更改隔离开来——如果你在设计一个界面,但不确定直接访问某个东西是否可行,那么你应该继续设计。一些库期望这样做,但并不多——反射、序列化、模拟对象都能很好地处理公共字段。通过继承这个类,您可以覆盖默认功能——换句话说,您不仅可以隐藏实现,而且可以使其不一致,从而真正混淆调用者。

最后三个我要离开(N/A或D/C)。。。

其他回答

DataStructure和Object之间存在差异。

数据结构应该暴露其内部而不是行为。

一个物体不应该暴露其内部,但它应该暴露其行为,这也被称为德米特定律

大多数DTO被认为是一种数据结构,而不是对象。他们应该只公开自己的数据,而不是行为。在数据结构中设置Setter/Getter将暴露行为,而不是其中的数据。这进一步增加了违反德梅特定律的可能性。

鲍勃叔叔在他的《干净的代码》一书中解释了得墨忒耳定律。

有一种著名的启发式方法叫做得墨忒耳定律,它说:模块不应该知道它的对象的内部结构操纵。正如我们在上一节中看到的,对象隐藏其数据并暴露操作。这意味着对象不应公开其通过访问器的内部结构,因为这样做是为了暴露,而不是隐藏其内部结构。更准确地说,德米特定律说C类的方法f应仅调用以下方法:Cf创建的对象作为参数传递给f的对象保存在C的实例变量中的对象该方法不应在任何允许的函数返回的对象上调用方法。换句话说,与朋友交谈,而不是与陌生人交谈。

因此,根据这一点,LoD违规的例子是:

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

在这里,函数应该调用它的直接朋友的方法,这里是ctxt,它不应该调用它直接朋友的朋友的方法。但该规则不适用于数据结构。所以在这里,如果ctxt、option、scratchDir是数据结构,那么为什么要用一些行为包装它们的内部数据,并违反LoD。

相反,我们可以这样做。

final String outputDir = ctxt.options.scratchDir.absolutePath;

这满足了我们的需求,甚至没有违反LoD。

灵感来源于Robert C.Martin(Bob叔叔)的“清洁代码”

因为从现在起2周(月、年),当您意识到setter需要做的不仅仅是设置值时,您还将意识到该属性已直接用于238个其他类:-)

如果您不需要任何验证,甚至不需要维护状态,即一个属性依赖于另一个属性,那么当一个属性发生更改时,我们需要维护状态。您可以通过公开字段而不使用getter和setter来保持简单。

我认为OOP会随着程序的增长而使事情变得复杂,这对于开发人员来说是一场噩梦。

一个简单的例子;我们从xml生成c++头。标头包含不需要任何验证的简单字段。但仍然像OOPS访问器一样,我们生成它们的方式如下。

const Filed& getfield() const
Field& getField() 
void setfield(const Field& field){...} 

这是非常冗长的并且不是必需的。一个简单的

struct 
{
   Field field;
};

足够且可读。函数式编程没有数据隐藏的概念,它们甚至不需要它,因为它们不会改变数据。

我花了很长时间来思考Java案例,我相信真正的原因是:

接口的代码,而不是实现接口只指定方法,不指定字段

换句话说,在接口中指定字段的唯一方法是提供一个用于写入新值的方法和一个用于读取当前值的方法。

这些方法是臭名昭著的getter和setter。。。。

代码不断演变。private非常适合需要数据成员保护的情况。最终,所有的类都应该是一种“小程序”,它有一个定义良好的接口,你不能只使用它的内部结构。

也就是说,软件开发并不是要设置课程的最终版本,就好像你在第一次尝试时按下了一些铸铁雕像一样。当你使用它时,代码更像粘土。它随着你的发展而发展,并进一步了解你正在解决的问题领域。在开发过程中,类之间可能会发生不应有的交互(您计划排除的依赖关系)、合并或拆分。因此,我认为争论归结为人们不想虔诚地写作

int getVar() const { return var ; }

所以你有:

doSomething( obj->getVar() ) ;

而不是

doSomething( obj->var ) ;

getVar()不仅在视觉上很嘈杂,而且给人一种错觉,认为getingVar(()在某种程度上是一个比实际更复杂的过程。如果你的类有一个passthrough setter,那么你(作为类编写者)如何看待var的神圣性对你的类的用户来说尤其令人困惑——那么你似乎在设置这些门来“保护”你坚持认为有价值的东西,(var的神圣性)但即使你承认,任何人只要进来并将var设置为他们想要的任何值,而你甚至不去偷看他们在做什么,那么var的保护就没有多大价值。

所以我按如下方式编程(假设采用“敏捷”类型的方法——即当我编写代码时不知道它将要做什么/没有时间或经验来规划一个复杂的瀑布式界面集):

1) 从具有数据和行为的基本对象的所有公共成员开始。这就是为什么在我所有的C++“示例”代码中,你会注意到我到处使用结构而不是类。

2) 当对象对数据成员的内部行为变得足够复杂时(例如,它喜欢以某种顺序保存内部std::list),就会编写访问器类型函数。因为我是自己编程的,所以我并不总是立即将成员设置为私有,但是在类的进化过程中,成员将被“提升”为受保护或私有。

3) 完全充实并对其内部有严格规则的类(即,它们确切地知道自己在做什么,你不能“操”(技术术语)它的内部)被赋予类名称,默认为私有成员,只有少数成员被允许公开。

我发现这种方法可以让我避免在一个类进化的早期阶段,当大量数据成员被迁移、转移等时,坐在那里虔诚地编写getter/setter。