干净代码建议在“格式”一章的“垂直距离”部分中避免使用受保护的变量:紧密相关的概念应在垂直方向上彼此靠近。显然,此规则不适用于属于单独文件的概念。但是,除非您有充分的理由,否则不应将紧密相关的概念分成不同的文件。的确,这是应避免使用受保护变量的原因之一。


是什么原因?

评论

在您认为需要的地方显示示例

我不会把那本书当作福音。

@Rig,您将在这些部分中亵渎亵渎,并带有类似的评论。
我可以。我已经读过这本书,对此可以有自己的见解。

我已经读过这本书,尽管我确实同意其中的大部分内容,但我也不会说我认为这是福音。我认为每种情况都是不同的..对于许多项目来说,要坚持书中所代表的确切准则是非常困难的。

#1 楼

应该避免使用受保护的变量,因为:


它们倾向于导致YAGNI问题。除非您的后代类实际上对受保护的成员进行填充,否则将其设为私有。
它们往往会导致LSP问题。受保护的变量通常具有一些与之相关的固有不变性(否则它们将是公共的)。然后,继承者需要维护人们可能搞砸或故意侵犯的那些属性。
他们倾向于侵犯OCP。如果基类对受保护成员的假设过多,或者继承者对类的行为过于灵活,则可能导致该扩展修改基类的行为。
它们往往导致继承而非扩展。这往往导致更紧密的耦合,更多的违反SRP,更困难的测试,以及“有利于继承而不是继承”讨论中的许多其他事情。这些都是“倾向于”的。有时,受保护的成员是最优雅的解决方案。受保护的功能往往很少出现这些问题。但是有很多事情使他们受到谨慎对待。任何需要这种注意的事情,人们都会犯错,在编程世界中,这将意味着错误和设计问题。

评论


感谢您有很多充分的理由。但是,这本书在格式和垂直距离的上下文中特别提到了它,这就是我想知道的部分。

–马特斯曼
2012年8月28日在16:12

似乎鲍勃叔叔是指某类之间(而不是同一类中)受保护成员的垂直距离。

–罗伯特·哈维(Robert Harvey)
2012年8月28日在16:18

@Matsemann-当然。如果我没记错的话,那部分将重点放在代码的可读性和可发现性上。如果变量和函数在概念上起作用,则它们应在代码中紧密结合。受保护的成员将由派生类型使用,由于它们位于完全不同的文件中,因此派生类型不一定接近受保护的成员。

– Telastyn
2012年8月28日在16:32

@ steve314我无法想象基类及其所有继承者只能生活在一个文件中的情况。如果没有示例,我倾向于认为这是对继承的滥用或对抽象的不良理解。

– Telastyn
2012年8月28日在22:08

YAGNI =您将不再需要LSP = Liskov替代原则OCP =开放/关闭原则SRP =单一责任原则

–布莱恩·格拉泽(Bryan Glazer)
2013年12月11日在1:48



#2 楼

与避免全局变量的原因相同,只是规模较小。这是因为很难在任何地方都找到正在读取(或更糟)的变量,并且很难使用法保持一致。如果使用受到限制(例如在派生类中的一个明显位置编写并在基类中的一个明显位置读取),那么受保护的变量可使代码更清晰简洁。如果您想在多个文件中读写变量willy-nilly,最好将其封装。

#3 楼

我还没有读过这本书,但是我可以刺破鲍伯叔叔的意思。但是成员变量应该属于它们所包含的类。这是基本封装的一部分。将protected放在成员变量上会破坏封装,因为现在派生类可以访问基类的实现详细信息。在普通类上创建变量protected时,也会发生相同的问题。 >
protected string Name
{
    get { return name; }
    private set { name = value; }
}


这允许使用构造函数自变量从派生类中安全设置public,而无需暴露基类的实现细节。

评论


您使用受保护的二传手创建了一个公共财产。应该使用带有私有设置程序的受保护属性来解决保护字段。

–安迪
2012年8月28日在22:32

现在+1。我想也可以保护Setter,但是无论新值是否有效,base都可以强制执行的point ID。

–安迪
2012年8月28日在22:39

只是提供相反的观点-我将所有变量都保护在基类中。如果仅通过公共接口访问它们,则不应将其作为派生类,而应仅包含基类对象。但是我也避免层次结构超过2个类

–马丁·贝克特(Martin Beckett)
2012年8月30日4:42

受保护成员与公共成员之间存在巨大差异。如果BaseType公开了一个公共成员,则意味着所有派生类型必须具有相同的成员相同的工作方式,因为DerivedType实例可能会传递给接收BaseType引用并希望使用该成员的代码。相反,如果BaseType公开受保护的成员,则DerivedType可能希望访问base中的该成员,但是base只能是BaseType。

–超级猫
2012年10月8日23:33

#4 楼

Bob叔叔的论点主要是距离问题之一:如果您有一个对班级很重要的概念,请将该概念与该班级捆绑在该文件中。没有在磁盘上的两个文件中分开。

受保护的成员变量分散在两个地方,有点像魔术。您引用了此变量,但此处未定义...在哪里定义?于是狩猎开始了。最好完全避免使用protected,他的论点。

现在,我不认为规则应始终遵循字母(例如:您不应使用protected)。看看他所获得的精神...将相关内容捆绑到一个文件中-使用编程技术和功能来实现。我建议您不要过度分析,以免陷入细节。

#5 楼

这里已经有一些很好的答案。但是要补充一件事:

在大多数现代的OO语言中,将每个类放入自己的文件中是一个好习惯(在Java AFAIK中是必要的)(请不要对C ++有所了解通常有两个文件)。

因此,实际上不可能将函数保留在派生类中,而源代码中的基类的受保护变量在源代码中与变量定义“垂直接近”。但是理想情况下,应该将它们保存在此,因为受保护的变量是供内部使用的,这使得使用它们的函数通常是“紧密相关的概念”。

评论


不在Swift中。有趣的是,Swift并没有“保护”,但有“ fileprivate”,它取代了“保护”和C ++“朋友”。

– gnasher729
18-2-27在15:25

@ gnasher729-当然,Swift在其传统中有很多Smalltalk。 Smalltalk除了私有变量和公共方法外没有其他任何东西。 Smalltalk中的private表示private:即使是同一类的两个对象也无法访问彼此的变量。

–法律
18-2-27在15:46

#6 楼

基本思想是,声明为“保护”的“字段”(实例级变量)可能比必须的可见,而“保护”的程度则小于您想要的。 C / C ++ / Java / C#中没有访问修饰符,该修饰符等同于“只能由同一程序集中的子类访问”,因此,您可以定义自己的子程序,以访问程序集中的字段,不允许在其他程序集中创建的子级具有相同的访问权限; C#具有内部修饰符和受保护的修饰符,但是将它们组合在一起可使访问成为“内部修饰符”,而不是“内部修饰符”。因此,无论您写的是该孩子还是其他人,任何孩子都可以访问受保护的字段。因此,受保护成为了黑客的门户。在C#中,您可以将其设为只读,从而使值类型有效地保持不变,并且引用类型无法重新初始化(但仍然非常可变),仅此而已。因此,即使受到保护,您的孩子(您不能信任)也可以访问此字段并将其设置为无效字段,从而使对象的状态不一致(应避免使用)。

使用字段的公认方法是将它们设为私有,并使用属性和/或getter和setter方法访问它们。如果该类的所有消费者都需要该值,则使吸气剂(至少)公开。如果只有孩子需要,则保护吸气剂。

回答该问题的另一种方法是问自己。为什么子方法中的代码需要直接修改我的状态数据的能力?那关于那个代码怎么说?那是表面上的“垂直距离”参数。如果子代中有必须直接更改父代状态的代码,那么也许该代码应该首先属于父代?

#7 楼

这是一个有趣的讨论。

坦率地说,好的工具可以缓解许多这些“垂直”问题。实际上,在我看来,“文件”的概念实际上阻碍了软件开发-Light Table项目正在致力于解决这一问题(http://www.chris-granger.com/2012/04/12/light- table --- a-new-ide-concept /)。

将文件从图片中删除,受保护的范围变得更具吸引力。受保护的概念在SmallTalk之类的语言中变得最为清晰-任何继承都只是扩展了类的原始概念。它应该完成父类所做的所有事情,并拥有一切。在这种模型中,我看不出有任何理由导致私有变量不被保护。如果扩展类应该表示包括父类的所有行为的扩展(我认为应该如此,因此认为私有方法本质上是不好的),那么扩展类为什么也不能表示此类数据的存储呢? >
换一种说法-在任何我认为私有变量比受保护变量更适合的情况下,继承并不是解决问题的首要方法。

#8 楼

正如它所说的那样,这仅仅是原因之一,即逻辑链接的代码应该放在物理链接的实体(文件,包和其他文件)中。在较小的规模上,这与您没有放置一个类的原因相同,该类使用显示UI的类在程序包中进行数据库查询。

但是,不建议使用受保护的变量的主要原因是因为它们倾向于破坏封装。变量和方法应具有尽可能有限的可见性;有关更多参考,请参见Joshua Bloch的有效Java,条款13-“最小化类和成员的可访问性”。如果受保护的变量非常糟糕,则它们根本不会放在语言中。受保护字段imho的合理位置位于测试框架的基类内部(集成测试类对其进行了扩展)。

评论


不要以为因为某种语言允许这样做就可以了。我不确定允许保护区的确有上帝的道理;我们实际上不允许他们在我的雇主那里。

–安迪
2012年8月28日在22:35

@安迪你还没看我说的话;我说过不建议使用受保护的字段,并说明原因。显然,在某些情况下需要保护变量。您可能只希望在子类中有一个恒定的最终值。

–随机42
2012年8月29日在9:33

然后,您可能希望重新编写一些帖子,因为您似乎可以互换地使用变量和字段,并明确表示将保护consts这样的事情视为例外。

–安迪
12年8月29日在11:31

@Andy很明显,因为我指的是受保护的变量,所以我不是在谈论局部或参数变量,而是在谈论字段。我还提到了非恒定保护变量有用的情况。恕我直言,公司强制执行程序员编写代码的方式是错误的。

–随机42
2012年8月30日在6:41

如果很明显,我不会感到困惑和低落。该规则由我们的高级开发人员制定,我们决定运行StyleCop,FxCop并要求进行单元测试和代码审查。

–安迪
2012年8月30日14:39

#9 楼

我发现对JUnit测试使用受保护的变量会很有用。如果将它们设为私有,则在测试过程中如果没有反思就无法访问它们。如果要通过许多状态更改来测试复杂的过程,这将非常有用。

可以使用受保护的getter方法将它们设置为私有,但是如果变量仅在JUnit测试期间在外部使用,我不确定这是否是更好的解决方案。

#10 楼

是的,我同意这有点奇怪,因为(我记得)该书还将所有非私有变量都讨论为要避免的事情。

我认为保护修饰符的问题不仅在于是暴露给子类的成员,它也对整个包可见。我认为鲍勃叔叔在这方面是例外。当然,不能总是避开使用受保护的方法,因此也就不能避免使用受保护的变量限定。 ,这反过来又使整个垂直距离度量变得有些争议。