面向对象编程的最大优点之一是封装,而我们被教导的“真理”之一是成员变量应该始终设为私有,并通过访问器和修改器方法来使用,从而确保能够验证和验证更改。
然而,我很好奇这在实践中是否真的很重要。特别是,如果您有一个更复杂的成员变量(例如集合),那么只公开它而不是创建一堆获取集合键、添加/删除集合项等方法可能会非常诱人。
您通常遵循这个规则吗?您的答案是否取决于代码是为自己编写还是为他人使用?除了混淆之外,是否还存在其他更微妙的原因?
面向对象编程的最大优点之一是封装,而我们被教导的“真理”之一是成员变量应该始终设为私有,并通过访问器和修改器方法来使用,从而确保能够验证和验证更改。
然而,我很好奇这在实践中是否真的很重要。特别是,如果您有一个更复杂的成员变量(例如集合),那么只公开它而不是创建一堆获取集合键、添加/删除集合项等方法可能会非常诱人。
您通常遵循这个规则吗?您的答案是否取决于代码是为自己编写还是为他人使用?除了混淆之外,是否还存在其他更微妙的原因?
作为一个需要维护多年前其他人编写的代码的人来说,很清楚地认识到如果将成员属性声明为public,最终它会被滥用。我甚至听说过有人不同意使用访问器和修改器,因为这仍然不能真正实现封装的目的,即“隐藏类的内部工作方式”。这显然是个有争议的话题,但我的观点是,“将每个成员变量都声明为private,主要考虑类需要做什么(方法),而不是如何让人们更改内部变量。”
这要看情况。这是一个必须实事求是决定的问题。
假设我有一个用于表示点的类。我可以为X和Y坐标设置getter和setter,或者我可以将它们都设置为public,并允许自由读写数据。在我看来,这是可以接受的,因为这个类就像一个增强版的结构体——一个数据集合,可能附带一些有用的功能。
然而,在许多情况下,您不希望提供对内部数据的完全访问权限,并依靠类提供的方法与对象进行交互。一个例子是HTTP请求和响应。在这种情况下,允许任何人通过网络发送任何内容是不明智的,必须通过类方法进行处理和格式化。在这种情况下,该类被构思为一个真正的对象,而不是一个简单的数据存储。
这实际上取决于动词(方法)是否驱动结构,还是数据驱动结构。
这些(和其他)问题适用于您是否直接访问底层实现或仅复制所有底层方法。您应该公开必要的实现,而不多余。将实现保持私有使整个系统更易维护。
我倾向于尽可能将成员保持私有,并仅通过getter方法访问,即使在同一类中也是如此。我还尝试避免使用setter方法作为第一草稿,尽可能促进值类型对象的使用。在大量使用依赖注入时,您经常会拥有setter方法但没有getter方法,因为客户端应该能够配置对象,但(其他人)不需要知道实际配置情况,因为这是一个实现细节。
敬礼, Ollie
我倾向于严格遵循规则,即使只是我的代码。我真的很喜欢C#中的属性,因为这个原因。它使得控制赋值变得非常容易,但你仍然可以像使用变量一样使用它们。或者将set设置为私有,get设置为公有等。
C# 属性“模拟”公共字段。看起来很酷,语法确实加速了创建那些 get/set 方法的过程。
记住在对象上调用方法的语义。方法调用是一个非常高层次的抽象,可以由编译器或运行时系统以多种不同的方式实现。
如果您要调用的对象的方法存在于同一进程/内存映射中,则编译器或虚拟机可能会对其进行优化,直接访问数据成员。另一方面,如果对象存在于分布式系统中的另一个节点上,则无法直接访问其内部数据成员,但仍可以通过发送消息来调用其方法。
通过编写基于接口的代码,您可以编写不关心目标对象存在位置、如何调用其方法甚至是否使用相同语言编写的代码。
我发现有很多getter和setter是一种代码异味,这意味着程序的结构设计得不好。你应该查看使用这些getter和setter的代码,并寻找真正应该成为类的一部分的功能。在大多数情况下,类的字段应该是私有实现细节,只有该类的方法可以操作它们。
同时拥有getter和setter等同于该字段是公共的(当getter和setter是微不足道/自动生成时)。有时候,更好的做法可能是将字段声明为public,这样代码会更简单,除非你需要多态性或框架需要get/set方法(而且你无法更改框架)。
但也有一些情况下,拥有getter和setter是一个好的模式。一个例子:
当我创建应用程序的GUI时,我尝试将GUI的行为保持在一个类(FooModel)中,以便可以轻松进行单元测试,并将GUI的可视化放在另一个类(FooView)中,只能手动测试。视图和模型通过简单的粘合代码连接;当用户更改字段x的值时,视图调用模型上的setX(String),模型可能会引发某些其他部分已更改的事件,视图将使用getter从模型获取更新的值。public boolean isEmployeeStatusEnabled() {
return pinCodeValidation.equals(PinCodeValidation.VALID);
}
public EmployeeStatus getEmployeeStatus() {
Employee employee;
if (isEmployeeStatusEnabled()
&& (employee = getSelectedEmployee()) != null) {
return employee.getStatus();
}
return null;
}
public void setEmployeeStatus(EmployeeStatus status) {
getSelectedEmployee().changeStatusTo(status, getPinCode());
fireComponentStateChanged();
}
这一切都关乎于控制人们使用你所提供的内容的能力。你越是有控制权,就能够做出更多的假设。
此外,理论上你可以改变底层实现或其他什么东西,但由于大部分情况下它是:
private Foo foo;
public Foo getFoo() {}
public void setFoo(Foo foo) {}
这有点难以证明。