软件设计原则----开-闭原则(OCP)
?
软件实体(类、模块、函数等)应该是可以扩展的,同时还可以是不必修改的,更确切的说,函数实体应该:
(1)对扩展是开放的
当应用的需求变化时,我们可以对模块进行扩展,使其具有满足改变的新的行为。即:我们可以改变模块的功能
(2)对更改是封闭的
对模块进行扩展时,不必改动模块已有的源代码或二进制代码。
?
分析:
世界是变化的(而且变化很快),软件是对现实的抽象。---->软件必须能够扩展。如果任何修改都需要改变已经存在的代码,那么可能导致牵一发动全身现象,进而导致雪崩效应,使软件质量显著下降。实现OCP的关键是抽象:
例1:既不开放也不封闭的Client:
?
问题:
client和server都是具体类,接口与实现没有实现分离。如果我们想要让client调用一个新的server类,那么我们不得不修改client的源代码。从而带来编译、链接、部署等一系列的问题。
——client类中更多的描述了高层的策略,而Server类中是对这些策略的一种具体实现。
而接口是策略的一个组成部分,它与client端的关系更加密切。ClientInterface中定义了client期望Server做什么,而server具体类是对client这种要求的一种具体实现。OCP原则要求我们清晰地区分策略和策略的具体实现形式。允许扩展具体的实现形式(开放),同时将这种扩展与策略隔离开来,使其对上层的策略透明(封闭)。例2:
//---------shape.h-----------------emum ShapeType{circle,square};struct Shape{ShapeType itsType;};//---------circle.h-----------------struct Circle{ShapeType itsType;double itsRadius;CPoint itscenter;};//---------square.h-----------------struct Square{ShapeType itsType;double itsSide;CPoint itsTopLeft;};//---------drawAllShapes.cpp----------typedef struct Shape * ShapePointer;void DrawAllShapes(ShapePointer list[], int n){int i;for(i=0;i<n;i++){struct Shape* s=list[i];switch (s->itsType){case square:s->Square();break;case circle:s->DrawCircle();break;}}}例2的问题:
这个程序不符合OCP,如果需要处理的几何图形中再加入“三角形”将引发大量的修改。
僵化的增加Triangle会导致Shape、Square、Circle以及DrawAllShapes的重新编译和部署
脆弱的因为存在大量的既难以查找又难以理解的Switch和If语句,修改稍有不慎,程序就会莫明其妙的出错
牢固的想在一个程序中复用DrawAllShapes,都必须带上Circle、Square,即使那个程序不需要他们
?
例2 修改后的设计:
class Shape{public:virtual void Draw() const=0;};class Square:public Shape{public:virtual void Draw() const;};class Circle:public Shape{public:virtual void Draw() const;};void DrawAllShapes(Vector<Shape*>&list){vector<Shape*>::iterator i;for(i=list.begin();i!=list.end();i++)(*i)->Draw();}小结:
一般而言,无论模块多么“封闭”,都会存在一些无法对之封闭的变化 没有对所有变化的情况都封闭的模型OCP----封装思想的体现
对可变性的封装原则:
找到一个系统的可变因素,将之封装起来。 考虑你的设计中什么会发生变化-------对应思路:什么会导致设计改变具体的:
相应设计模式:
StrategySimple FactoryFactory MethodAbstract FactoryBuilderBridgeFa?adeMediator 1 楼 tamsiuloong 2011-10-08 对扩展是开放的 对更改是封闭的