开放封闭原则指出"软件实体(类,模块,功能等)应该是可以扩展的,但是对于修改是封闭的".
然而,Joshua Bloch在其着名的书"Effective Java"中给出了以下建议:"继承的设计和文档,或者禁止它",并鼓励程序员使用"final"修饰符来禁止子类化.
我认为这两个原则显然是相互矛盾的(我错了吗?).编写代码时遵循哪个原则,为什么?你是否打开你的课程,不允许继承你的课程(哪些?),或者尽可能使用最终修饰语?
坦率地说,我认为开放/封闭原则更不合时宜.它从80年代和90年代开始,当OO框架建立在一切必须从其他东西继承并且一切都应该是可子类化的原则的基础上.
这在MFC和Java Swing这个时代的UI框架中最为典型.在Swing中,你有一个荒谬的继承,其中(iirc)按钮扩展复选框(或相反的方式)给出其中一个未使用的行为(我认为它是复选框上的setDisabled()调用).为什么他们分享祖先?没有其他原因,他们有一些共同的方法.
这些天的成分比继承更受青睐.虽然Java默认允许继承,但.Net采取了(更现代的)默认禁用它的方法,我认为这更正确(并且更符合Josh Bloch的原则).
DI/IoC还进一步提出了组合案例.
Josh Bloch还指出继承打破了封装并提供了一些很好的例子.还有人证明,如果通过委派而不是扩展类来改变Java集合的行为更加一致.
就个人而言,我在很大程度上认为继承只不过是现在的实施细节.
我不认为这两个陈述相互矛盾.类型可以打开以进行扩展,并且仍然可以关闭以进行继承.
一种方法是使用依赖注入.类型可以在创建时提供这些类型,而不是创建自己的帮助器类型的实例.这允许您在不更改类型本身的情况下更改类型的部件(即打开以进行扩展)(即关闭以进行修改).
在开闭原则(打开扩展,关闭以进行修改)中,您仍然可以使用最终修饰符.这是一个例子:
public final class ClosedClass { private IMyExtension myExtension; public ClosedClass(IMyExtension myExtension) { this.myExtension = myExtension; } // methods that use the IMyExtension object } public interface IMyExtension { public void doStuff(); }
该ClosedClass
闭合的类内的修改,但开放用于通过另一个延伸部.在这种情况下,它可以是任何实现IMyExtension
接口的东西.这个技巧是依赖注入的变种,因为我们用另一个来提供封闭的类,在这种情况下是通过构造函数.由于扩展是一个interface
不可能final
但它的实现类可以.
使用final on classes在java中关闭它们类似于sealed
在C#中使用.在.NET方面有类似的讨论.
现在我默认使用final修饰符,几乎反射性地作为样板的一部分.当你知道给定的方法将始终如你正在查看的代码中看到的那样时,它会使事情更容易推理.
当然,有时会出现类层次结构正是您想要的情况,然后不使用它就很愚蠢.但要害怕超过两个级别的层次结构,或者非抽象类进一步子类化的层次结构.课程应该是抽象的或最终的.
大多数时候,使用构图是要走的路.将所有常用机器放入一个类中,将不同的案例放入不同的类中,然后将实例合并为一个整体.
你可以称之为"依赖注入",或"策略模式"或"访问者模式"或其他什么,但它归结为使用组合而不是继承来避免重复.