不!!!!不要点击生成 getter 和 setter 选项!!!
我喜欢这条规则:“不要使用访问器和修改器。” 像任何好的规则一样,这条规则注定要被打破。但当?
首先,让我清楚我在说什么:在考虑了一系列更好的替代方案之后,向 OO 类添加 getter 和 setter 应该是最后的手段。我相信仔细分析得出的结论是,在大多数情况下,getter 和 setter 是有害的。
有什么危害?首先让我指出,我所说的“伤害”可能根本没有任何伤害。在某些情况下,以下是一个完全合理的类:
// Car1.java
public class Car1 {
public Engine engine;
}
但是,请注意胃部收紧的感觉,头发的毛茸茸的感觉,以及看到类似事物时可能会感到肌肉紧张的感觉。
现在,我想指出该类(具有公共类成员的公共类)和下面的类(具有由 getter 和 setter 公开的私有成员的公共类)之间没有有意义的功能差异。在Car1.java和Car2.java这两个类中,我们得到的结果基本相同。
// Car2.java
public class Car2 {
private Engine engine;
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
}
为了说明这一点,我在Car1.java或Car2.java 中读写引擎:
// Car1 member read and write
Car1 car1 = new Car1();
logger.debug("Car1's engine is {}.", car1.engine);
car1.engine = new HemiEngine();
// Car2 member read and write
Car2 car2 = new Car2();
logger.debug("Car2's engine is {}.", car2.getEngine());
car2.setEngine(new HemiEngine();
这里的关键是,什么我可以做与Car2.java,我可以做Car1.java,反之亦然。 这很重要,因为我们被教导要在看到Car1.java时变得娇气。我们看到那个公众成员坐在那里,我们说,不安全! 发动机是不是受什么保护!任何人都可以做任何事情!啊啊啊啊啊啊啊啊!
然而,出于某种原因,当我们看到Car2.java时,我们松了一口气。这 - 对不起 - 我个人认为很有趣,因为这两件事实际上有相同(不存在)的保护措施。
什么可能出错?以下是直接公开单个私有成员、具有相同名称且不提供其他功能的公共 getter 和 setter 的一些缺点。
getter 和 setter 是孤立变化的虚假保险单
getter 和 setter 的一个假定优点是,如果类成员的类型需要更改,更改可以限制在类内部,方法是使现有的 getter 简单地从内部类型转换为先前公开的类型类型。
// Car2.java, engine changed to motor
public class Car2 {
private Motor motor;
public Engine getEngine() {
return convertToEngine(motor);
}
public void setEngine(Engine engine) {
this.motor = convertToMotor(engine);
}
}
我的问题是工作程序员多久必须这样做?我不记得在我多年的软件生涯中曾经这样做过。我一次也没有能够利用 getter 和 setter 提供的虚假保险单。
此外,如果engine
从未公开过(假设它是私有的或包私有的),那么这个论点就变得完全没有实际意义。只需公开行为,而不是状态,您就无需担心更改实现的灵活性。
私有成员不应该被暴露的这种认识触发了另一种认识,即这个论点是同义反复的。getter 和 setter 公开了私有成员,并将其存在的理由建立在私有成员被公开的事实上。
getter 和 setter 公开实现细节假设我只为我的 Car 对象提供了以下 API:
_________________________________
| Car |
|---------------------------------|
| + getGasAmount(): Liters |
| + setGasAmount(liters: Liters) |
|_________________________________|
如果您假设这是一辆以升为单位在内部跟踪汽油的汽油动力汽车,那么您在 99.999% 的时间里都是对的。这真的很糟糕,这就是 getter 和 setter 公开实现/违反封装的原因。现在这段代码很脆弱,很难改变。如果我们想要一辆氢燃料汽车怎么办?我们现在必须扔掉整个 Car 类。拥有像fillUp(Fuel fuel)
.
诸如此类的事情就是著名图书馆拥有糟糕的遗留类的原因。你有没有注意到大多数语言都有一个Dictionary
数据结构,但它是Map
在 Java 中调用的?Dictionary
实际上是JDK 1.0中引入的一个接口,但是它有问题,最终不得不被替换为Map
.
给你讲一个关于朋友的故事。好的?我说的朋友!!!
有一天,这位朋友上班,发现世界各国的几十个知名网站都有母公司主网站(不是他们自己的)的页眉和导航,而且都是英式英语。运营团队疯狂地重新启动了全球数百台服务器,因为在这些服务器运行的前半小时左右,一切正常。然后(砰!)突然之间会发生一些事情,使整个事情变得横向。
罪魁祸首是所有这些不同站点都在使用的共享平台深处的setter 方法。一小段按计划运行的代码碰巧最近在这次惨败中更新,通过调用这个 setter 改变了确定站点标题和语言的基础值。
如果你只有一个吸气剂,事情可能同样糟糕。至少在 Java 中,从 getter 返回引用类型为调用者提供了该引用,现在它可以由调用者以意想不到的方式进行操作。让我示范一下。
public class Debts {
private List debts;
public List getDebts() {
return debts;
}
}
好吧,这似乎很合理。我需要能够看到一个人的债务才能给他们一个声明。嗯?你说什么?现在可以加债了吗?拉屎!那是怎么发生的!
Debts scottsDebts = DebtTracker.lookupDebts(scott);
List debts = scottsDebts.getDebts();
// add the debt outside scotts debts, outside the debt tracker even
debts.add(new Debt(new BigDecimal(1000000)));
// prints a new entry with one million dollars
DebtTracker.lookupDebts(scott).printReport();
哎呀!
防止这种情况的一种方法是返回一个副本。另一种方法是拥有一个不可变的成员。但是,最好的方法是根本不以任何方式公开成员,而是将操纵该成员的行为引入类中。这实现了实现的完全隔离,并且只创建了一个可以更改的地方。
当吸气剂有意义时等一下!如果访问器和修改器有这么多缺点,为什么还要使用它们?
我确信只返回类成员的 getter 和 setter 几乎没有任何意义。但是,只要您实际在该方法中执行某些操作,您就可以编写一些接近getter/setter 功能的内容。
两个例子:
-
在 setter 中,在根据某些输入更新此对象中的状态之前,我们验证输入。输入验证是附加功能。
-
getter 的返回类型是一个接口。因此,我们将实现与公开的接口分离。
看,我在这里真正提倡的是对 getter 和 setter 的不同立场和哲学。与其说永远不要使用访问器和修改器,我想为您提供我在使用之前尝试用尽的选项列表:
-
我的“默认”是从私有最终成员开始,仅由构造函数设置。没有 getter 或 setter!
-
如果另一个班级绝对需要看到这个成员,我想想为什么。我尝试查看是否存在可以公开的行为,并为该行为创建一个方法。
-
如果出于某种原因绝对有必要,那么我会放松到包私有(在 Java 中)并将成员仅公开给同一个包中的其他类,但不会再公开。
-
好的,数据用例呢?从字面上看,我可能需要一个对象来跨某种接口边界传递数据(比如文件系统、数据库、Web 服务或其他东西)。我仍然不喜欢 getter 和 setter。我用所有包私有成员创建了这个类,我认为它只是一个属性包。我尝试在应用程序的边界将它们限制在它们自己的包和层中。
-
我会考虑在公共 API 中为数据用例创建一个 getter 和一个 setter,比如假设我正在编写一个旨在用作许多其他应用程序依赖项的库。但我只会在用尽所有这些列表项后才考虑它。
一个简短的后记。显然,世界上存在关于 getter 和 setter 的争论。重要的是要知道,显然有一个像 Robert C.(“鲍勃叔叔”)马丁这样的“大师”阵营,他们支持避免 getter 和 setter。在Clean Code一书中,Martin 在第 6 章中写道:
Bean 具有由 getter 和 setter 操作的私有变量。bean 的准封装似乎让一些 OO 纯粹主义者感觉更好,但通常没有其他好处。
Josh Bloch 在Effective Java 的 第 14 条中有着微妙的立场,稍微支持公共类的 getter 和 setter,而稍微反对其他类。他最终基本上说他真正关心的是可变性,我在上面提到了这一点:
总之,公共类不应该暴露可变字段。公共类公开不可变字段的危害较小,但仍然值得怀疑。然而,有时需要包私有或私有嵌套类公开字段,无论是可变的还是不可变的。
进一步阅读
以下是比我更聪明的人的一些有用的想法。
告诉,不要问
为什么 getter 和 setter 方法是邪恶的
访问者是邪恶的