我很少使用继承,但是当我这样做时,我从不使用受保护的属性,因为我认为它打破了继承类的封装。
您使用受保护的属性吗?你用它做什么?
答案 0 :(得分:19)
在Bill Venna的设计interview中, Effective Java 的作者Joshua Bloch说:
信任子类
Bill Venners: 我是否应该更亲密地信任子类 无子?例如,我做 子类更容易 执行打破我比我 会不是一个非子类?在 特别是,你感觉如何 受保护的数据?
Josh Bloch:写一些既可子类又健壮的东西 对抗恶意子类是 实际上是一件非常艰难的事情, 假设您授予子类访问权限 到您的内部数据结构。如果 子类无权访问 普通用户的任何东西 没有,那么它就更难了 子类做损坏。但除非你 让你所有的方法都是最终的, 子类仍然可以打破你的 只是做错了合同 回应方法的事情 调用。这正是为什么 像String这样的安全关键类 是最后的。否则有人可以 写一个创建字符串的子类 似乎是可变的,这将是 足以打破安全。那么你 必须信任你的子类。如果你 不信任他们,那么你不能允许 他们,因为子类可以这么容易 导致一个类违反它 合同。
就受保护的数据而言, 这是一个必要的邪恶。它应该是 保持在最低限度。最受保护的数据 和受保护的方法相当于 承诺实施 详情。受保护的字段是 你的实施细节 使子类可见。甚至一个 受保护的方法是一块 你正在制作的内部结构 对子类可见。
你让它可见的原因是 它通常是必要的,以便允许 子类做他们的工作,或做 它有效。但是一旦你完成了 它,你致力于它。就是现在 你不被允许的东西 改变,即使你后来发现更多 有效的实施,没有 更长的时间是使用a 特定的领域或方法。
所以其他所有事情都是平等的,你 不应该有任何受保护的成员 一点都不但是那说,如果你也有 很少,那么你的课程可能无法使用 作为超级班,或至少不是 一个高效的超级课程。经常你 事后发现。我的哲学 就是拥有尽可能少的受保护成员 当你第一次写的时候可能 类。然后尝试将其子类化。您 可能会发现没有特别的 protected方法,所有子类都将 必须做一些坏事。
举个例子,如果你看一下
AbstractList
,你会发现那里 是一种删除a的受保护方法 一次性列表的范围 (removeRange
)。为什么那里? 因为正常的成语要删除一个 范围,基于公共API,是 致电subList
以获取子List
, 然后在其上调用clear
子 -List
。没有这个特别的 然而,受保护的方法是唯一的clear
可以做的事情 反复删除个别元素。想一想。如果你有一个阵列 代表,它会做什么?它 将反复折叠数组, 做N次N次。所以它会 接受一定数量的工作, 而不是线性的工作量 它应该。提供这个 受保护的方法,我们允许任何 有效的实施 删除整个范围即可。和 任何合理的
List
实施 可以更有效地删除范围 一下子。我们需要这个受保护的 方法是你必须要做的 要比我更聪明才能知道 面前。基本上,我实施了 事情。然后,当我们开始子类化时 它,我们意识到范围删除是 二次。我们负担不起,所以 我放入了受保护的方法。我认为 这是最好的方法 保护方法。放入尽可能少的 可能,然后根据需要添加更多。 受保护的方法代表 对您可能的设计承诺 想要改变。你总是可以添加 受保护的方法,但你不能采取 他们出去了。
Bill Venners: 受保护的数据?
Josh Bloch:同样的事情,但更多。受保护的数据甚至更多 弄乱你的危险 数据不变量。如果你给某人 否则访问一些内部数据, 他们对此有自由的统治权。
简短版本:它打破了封装,但这是一个必须保持最低限度的必要之恶。
答案 1 :(得分:7)
C#:
我将protected用于抽象或虚拟方法,我希望基类重写。如果它可以被基类调用,我也会使方法受到保护,但我不希望它在类层次结构之外调用。
答案 2 :(得分:6)
您可能需要它们用于静态(或“全局”)属性,您希望您的子类或来自相同包的类(如果它是关于java)从中受益。
那些表示某种“常量值”的静态最终属性很少有getter函数,因此受保护的静态final属性在这种情况下可能有意义。
答案 3 :(得分:3)
Scott Meyers说不在Effective C ++中使用受保护的属性(第3版):
第22项:将数据成员声明为私有。
原因与您给出的相同:它打破了封装。结果是,否则对类布局的局部更改可能会破坏依赖类型,并导致许多其他位置发生更改。
答案 4 :(得分:2)
拥有受保护的属性从来没有任何充分的理由。基类必须能够依赖于状态,这意味着通过访问器方法限制对数据的访问。你不能让任何人访问你的私人数据,甚至是儿童。
答案 5 :(得分:2)
protected
关键字是一个概念性错误和语言设计版本,以及一些现代语言,如Nim和Ceylon(请参阅http://ceylon-lang.org/documentation/faq/language-design/#no_protected_modifier),这些语言经过精心设计而不仅仅是复制常见错误,没有这样的关键字。
它不是破坏封装的受保护成员,它暴露了不应暴露的成员破坏了封装...无论它们是受保护还是公开都无关紧要。 protected
的问题在于它是错误的和误导性的......声明成员protected
(而不是private
)不能保护它们,它恰恰相反,{{1}确实。受保护的成员可以在类外部访问,它暴露给世界,所以它的语义必须永远保持,就像public
的情况一样。 “受保护”的整个想法是无稽之谈......封装不是安全性,关键字只会加剧两者之间的混淆。你可以通过避免在你自己的类中使用public
来帮助一点 - 如果某些东西是实现的内部部分,不是类的语义的一部分,并且将来可能会改变,那么就可以私有或内部的包,模块,程序集等。如果它是类语义的一个不可更改的部分,那么公开它,然后你不会惹恼你的类的用户谁可以看到有一个有用的成员文档但不能使用它,除非它们正在创建自己的实例并且可以通过子类化来实现它。
答案 6 :(得分:1)
我不在Java中使用受保护的属性,因为它们只受到包保护。但是在C ++中,我将在抽象类中使用它们,允许继承类直接继承它们。
答案 7 :(得分:1)
通常,您真的不想使用受保护的数据成员。如果您编写API,这是双重的。一旦有人从你的班级继承,你就永远不会真正做到维护,也不会以某种奇怪的,有时是狂野的方式打破它们。
答案 8 :(得分:1)
我认为受保护的属性是个坏主意。我使用CheckStyle与我的Java开发团队一起执行该规则。
答案 9 :(得分:1)
我最近在一个项目上做过“受保护”的成员是一个非常好的主意。班级组织就像是:
[+] Base
|
+--[+] BaseMap
| |
| +--[+] Map
| |
| +--[+] HashMap
|
+--[+] // something else ?
Base实现了一个std :: list但没有别的。用户禁止直接访问列表,但由于Base类不完整,因此它依赖于派生类来实现对列表的间接。
间接可能来自至少两种风格:std :: map和stdext :: hash_map。两个映射的行为方式都相同,但事实上hash_map需要Key可以清除(在VC2003中,可转换为size_t)。
因此BaseMap将TMap实现为模板化类型,它是一个类似地图的容器。
Map和HashMap是BaseMap的两个派生类,一个在std :: map上专门定位BaseMap,另一个在stdext :: hash_map上。
所以:
Base无法使用(没有公共访问者!),只提供了常用功能和代码
BaseMap需要轻松读/写std :: list
Map和HashMap需要对BaseMap中定义的TMap进行简单的读/写访问。
对我来说,唯一的解决方案是对std :: list和TMap成员变量使用protected。我无法将这些“私有”放进去,因为无论如何我都会通过读/写访问器公开所有或几乎所有的功能。
最后,我想如果你把你的类划分为多个对象,每个派生为其母类添加所需的特性,并且只有最可用的派生类,那么保护就是要走的路。事实上,“受保护的成员”是一个阶级,因此,几乎不可能“打破”,帮助。
但是,否则应尽可能避免受保护(即:默认情况下使用private,并且必须公开方法时公开)。
答案 10 :(得分:0)
我用它们。简而言之,如果您想要共享一些属性,这是一个好方法。当然,您可以为它们编写set / get函数,但如果没有验证,那么重点是什么?它也更快。
考虑一下:你有一个类是你的基类。它有很多属性你不想在子对象中使用。你可以为每个编写一个get / set函数,或者你可以设置它们。
我的典型示例是文件/流处理程序。您想要访问处理程序(即文件描述符),但是您想要将其从其他类中隐藏。这比为它编写set / get函数容易。
答案 11 :(得分:0)
总的来说,是的。受保护的方法通常更好。
在使用中,通过对一个类的所有子级共享的对象使用受保护的 final 变量,可以实现一定程度的简单性。我总是建议不要将它与原语或集合一起使用,因为合同不可能为这些类型定义。
最近,我开始将您使用原型和原始集合的内容与您使用格式良好的类进行分离。原始人和收藏品应该始终是私密的。
此外,我偶尔会在公共成员变量声明为final时公开它们,并且是格式良好的类,它们不太灵活(同样,不是原语或集合)。
这不是一些愚蠢的捷径,我认真地认真思考并且认为公开最终变量暴露对象和吸气剂之间绝对没有区别。
答案 12 :(得分:0)
这取决于你想要什么。如果你想要一个快速类,那么应该保护数据并使用protected和public方法。 因为我认为您应该假设从您的班级派生的用户非常了解您的课程,或者至少他们已经在他们要覆盖的功能上阅读了您的手册。
如果您的用户弄乱了您的课程,那不是您的问题。在覆盖其中一个虚拟机时,每个恶意用户都可以添加以下行:
(C#)
static Random rnd=new Random();
//...
if (rnd.Next()%1000==0) throw new Exception("My base class sucks! HAHAHAHA! xD");
//...
你不能密封每一堂课以防止这种情况。
当然,如果您想要对某些字段进行约束,那么请使用存取函数或属性或所需的内容,并将该字段设为私有,因为没有其他解决方案......
但我个人不喜欢不惜一切代价坚持oop原则。特别是制作属性的唯一目的是使数据成员保密。
(C#):
private _foo;
public foo
{
get {return _foo;}
set {_foo=value;}
}
这是我个人的意见。
但要做你老板要求的事情(如果他想要私人领域而不是那样做。)
答案 13 :(得分:0)
我在基类中使用受保护的变量/属性,我知道我不打算更改为方法。这样,子类可以完全访问其继承的变量,并且没有(人为创建的)通过getter / setter访问它们的开销。一个例子是使用底层I / O流的类;没有理由不允许子类直接访问底层流。
这适用于在基类和所有子类中以直接简单方式使用的成员变量。但是对于使用更复杂的变量(例如,访问它会导致类中其他成员的副作用),直接可访问的变量是不合适的。在这种情况下,可以将其设为私有,并且可以提供公共/受保护的getter / setter。一个例子是基类提供的内部缓冲机制,直接从子类访问缓冲区会损害基类用来管理它们的算法的完整性。
这是一个设计判断决策,基于成员变量的简单程度,以及未来版本中预期的结果。
封装非常好,但可以采取太多措施。我见过自己的私有方法只使用getter / setter方法访问其成员变量的类。这是过度的,因为如果一个类不能信任自己的私有方法和它自己的私有数据,谁可以信任呢?