我的问题与Java有关,但它也可以应用于C#。我想知道为什么每个人都建议将实例变量私有而不是让它们受保护。
让我们考虑一下。子类看不到私有变量,所以如果我需要访问或更改子类中超类的变量,我被迫使用一些访问器和mutators方法,如getMyPrivateVariable
或setMyPrivateVariable
。但是,当您扩展某个类并继承其成员时,这就像您直接在子类中声明它们一样。从逻辑上讲,这意味着子类也应该直接访问实例变量,并为使用受保护变量设计类提供了理由。我理解这种做法会破坏封装,但这在继承的情况下似乎无关紧要,因为在这种情况下,一切都像超级类的成员在sublcass中声明一样,所以sublcass有一个“自然权利”能够直接访问其成员,无论他们是否继承。在我看来,封装对于通过对象的继承树之外的其他对象与对象进行交互更为重要。
所以,我的问题是为什么每个人都建议将类的实例变量声明为私有而不是受保护?
答案 0 :(得分:1)
你自己回答 - 封装。
例如,您有一个抽象的Cat类。这定义了成员变量的速度 - 即它的运行速度。
抽象类还定义了最终的运行方法,显然可以更新速度。
现在 - 如果Cat的子类 - 例如Moggy或Tabby可以直接访问并修改“速度”,然后它可以打破run方法。
最好保持它在开始的地方。如果需要,您也可以在本地声明它。
答案 1 :(得分:0)
如果我需要访问或更改子类中的继承变量,我被迫使用一些访问器[...]
你错了:private
成员不继承。你可能会混淆"继承"的含义。事实上,子类的实例拥有其超类的所有成员。
查看私有成员未被继承意味着什么的最好方法是观察子类可以声明自己的私有成员,这永远不会导致与超类的同名私有成员发生冲突。两人和平共处。
仅在设计要由子类化使用的 specpically 类时才声明成员protected
:在这种情况下,protected
成员实际上是其公共API的一部分。大多数类不设计用于公共扩展,但声明它们final
通常不是一种选择。如果这些类具有其所有成员protected
,则等同于使它们成为public
,因为任何客户端代码都可以轻松地创建其虚拟子类,而不是向父类添加任何内容,而是暴露其内部。
答案 2 :(得分:0)
考虑以下“经验法则”:从课堂外看到的任何内容都会成为外部世界合同的一部分。为了让您自由地按照自己的意愿修改代码,并且影响最小,您的目标是将可见性限制在最低限度 - 人们通常不会限制他们的自由,除非他们被迫。
换句话说,您必须有理由宣布成员不是private
。一旦你有充分的理由,只需将可见性提高到最小可能范围(首选应该是包私有 / 默认可见性)。这是因为您应该完全控制(或至少控制)包中的代码,因此可以单独执行更改。作为旁注,“package private”是唯一可以应用于顶级类本身的可见性级别(public
除外)。因此,您应该考虑从未在其包外使用的类中删除public
修饰符。
protected
可见性“逃避”您的控制范围,因为任何客户都可能依赖于该特定变量或方法。
至于public
,一个好的做法是只将它用于你实现的接口中声明的方法。
答案 3 :(得分:0)
如果你保护你的实例变量并且你可以将你的类子类化(因为它不是final
)那么每个人都可以继承你的类并在你不期望的时候更改你的实例变量。这极大地增加了您的功能无法按照您希望的方式运行的机会。您必须处理各种类的使用方法,这使您很难确保实例变量的每个可能的更改在任何可能的更改时间点产生预期结果。
答案 4 :(得分:0)
通过getter和setter公开它们,您可以保证它们将被正确访问(例如,没有超出范围的值)关于您的类的角色。你也正在为将来的干预做准备,通过隔离责任(例如,如果在某些时候你意识到某些处理应该系统地完成一个特定的变量,那么你只有一种方法可以改进,理论上不会触及其他任何东西,更重要的是不会破坏整个程序的功能)。通过仅提供一个getter方法,您也可以自由地将其中一些定义为只读。
所以基本上它增加了一层安全性,让你可以更好地控制你的变量。
此外,由于它们被称为实例变量,因此对我来说只有实际实例才能直接访问它们。
答案 5 :(得分:0)
OOP的本质是发送消息,这意味着将行为优先于数据。
避免暴露变量的原因是让您的软件尽可能灵活。
客户不应该从任何地方获取一些数据并自己实施逻辑(逻辑不属于他们的责任),他们应该Tell! Don't Ask!。 他们不应该掌握所需的算法,应该是目标类的角色。
通过让你的变量protected
在基类(或最终类......)中,你允许子类改变它们,而且很多时候,子类可能没有知识,也没有责任去做。
它经常会导致一些神秘的错误,比如从子类为字段的基类分配一个意外的值。
当然,某些情况下需要protected
,但只有当子类担心同样的责任时才需要。{/ p>
因此,始终使您的变量/方法范围尽可能具有限制性。因此,您的代码将受到保护,灵活,易于单元测试(因为集中在一个地方),并且在将实例变量暴露给客户端时可以免除许多常见错误。尤其是,您的班级可以在不破坏客户端代码的情况下更改其内部。
此外,当您不处理数据结构时,请避免使用getter / setter。实际上,这将促进行为方法。
实际上,在95%的情况下,getter / setter非常差(没有实现有价值的逻辑),并且只是最终运行以使变量public
/ protected
。