重新学习面向对象设计之开放 - 封闭原则
作为一名程序猿,每个人心中有一个小窃喜:自己的代码从产品的第一版本开始永存,也已有一点小愤怒:自己的代码经常被重构掉,还经常被说道,这代码写的真差。作为一位15+的老码农,也曾经历各种代码情绪,也会因为自己的代码被认可而开心,也会因自己的代码造成损失而懊恼。面向对象设计原则之开放-封闭原则
正是解决这个问题的。
一、什么是开放-封闭原则
定义:开放-封闭原则,Open Closed Principle,OCP,Software entities (classes, modules, functions) should be open for extension but closed for modification. 软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的。
OCP原则设计出的模块具有两个主要的特征:
对于扩展是开放的(Open for extension)。模块对扩展开放,就意味着需求变化时,可以对模块扩展,使其具有满足那些改变的新行为。换句话说,模块通过扩展的方式去应对需求的变化。
对于更改是封闭的(Closed for modification)。模块对修改关闭,表示当需求变化时,关闭对模块源代码的修改,当然这里的“关闭”应该是尽可能不修改的意思,也就是说,应该尽量在不修改源代码的基础上面扩展组件。
这两个特征好像是互相矛盾的。扩展模块行为的通常方式就是修改该模块的源代码。不允许新修改的模块常常都被认为是具有固定的行为。
二、如何实现OCP
怎样可能在不改动模块源码的情况下去更改它的行为呢?怎么才能在无需对模块进行改动的情况下就改变它的功能呢?关键是抽象。
在Java、C++等面向对象语言中,可以创建出固定却能描述一组任意个可能行为的抽象体,这个抽象体就是抽象基类,而这一组任意个可能的行为则表现为可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,所以它对于更改可以是关闭的,同时,通过从这个抽象体派生,也可以扩展此模块的行为。
上述这段话来自《敏捷软件开发》,听起来还有有些抽象难以理解,下面我们通过一个例子来了解一下。
2.1 不遵守OCP
图1 不开放又不封闭的Client
图1中展示了一个简单的不遵循OCP的设计。Client类和Server类都是具体类。Client类使用Server类,如果我们希望Client对象调用另外一个不同的Server对象,那么就必须要把Client类中使用Server类的地方更改为新的Server类。
2.2 遵循OCP的例子
图2 遵循OCP的Client
图2是针对图1问题的改进,采用了策略模式,增加了ServerInterface接口类,client类调用ServerInterface接口来,不关心具体是哪个Server类对象。当需要调用另外一个Server类对象时,只要再实现一个ServerInterface的Server类就可以了,无需对Client类做任何修改。
2.3 再改进
是不是以上设计100%遵循OCP了?这个不一定,需要看具体的需求变化,更确切的来说,很难有100%的OCP。这么说可能会更糊涂了,到底怎么算是遵循OCP了呢?到底怎么做才算OK呢?这是需要需求、成本、质量进行综合评估和衡量。但有些是可以做的。
1、预测变化
既然不可能完全封闭,那么就必须有策略地对待这个问题,也就是说,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择,必须先猜测出最有可能给发生的变化种类,然后构造抽象来隔离那些变化。
这需要设计人员具备 一些从经验中获得的预测能力。有经验的设计人员希望自己对用户和应用领域很了解, 能够以此来判断各种变化的可能性,然后他可以让设计对于最有可能发生的变化遵循OCP原则。
要做到这样是非常不容易的,而且也需要付出高昂的代价,我们需要设定一个度,防止过于复杂以及过高的成本。
架构师思维:预测最可能变化的地方,采用合适的设计模式,至少能够满足当前需求和未来一定版本数。
2、允许错误
当无法预测变化时,我们可以运行一次错误。如果不发生需求变化,那我们就不用过多的设计,满足当前需求的设计遵循OCP就可以。
三、总结
总的来说,OCP是面向对象设计的核心之一,遵循OCP原则可以带来面向对象计生户所声称的好处(灵活性、可重用性会和可维护性)。但并不是说需要随意的进行抽象,这还会带来另外的一些问题。
正确的做法是,开发人员应该仅仅对程序中呈现出频繁变化的那些部分作出抽象。
拒绝不成熟的抽象和抽象本身一样重要。
版权声明: 本文为 InfoQ 作者【IT老兵重开始】的原创文章。
原文链接:【http://xie.infoq.cn/article/a02fd8505a45ec8c8a608bde8】。
本文遵守【CC BY-NC】协议,转载请保留原文出处及本版权声明。
评论