|
原文是C++ VIEW第二期的一篇译文,这里做个总结,便于查阅。
开放封闭原则
系统在添加新的需求的时候能够尽可能做到,只是添加新的代码 open for extension,而不需要修改原有模块代码 closed for modification
通过提取基类的方法,client 调用server 抽象基类abstract server的抽象接口,从而更换不同sever的时候,client的调用server的代码都不需要改动,接口不变,
只是server内容变化。
例子,
一个绘制函数,要求能够针对输入的不同对象,调用不同的绘制函数,如能够绘制矩形,圆形,适当调用矩形绘制函数,圆形绘制函数。
1.用c语言实现
这个例子其实给出了,c语言模拟c++类继承的方法。利用指针的强制转换,因为指针仅仅是地址可以指向任何对象,利用指针强制转换,告诉编译器具体按什么对象处理指针所指。
Listing 1
/*Procedural Solution to the Square/Circle Problem*/
enum ShapeType {circle, square};
struct Shape
{
ShapeType itsType;
};
struct Circle
{
ShapeType itsType;
double itsRadius;
Point itsCenter;
};
struct Square
{
ShapeType itsType;
double itsSide;
Point itsTopLeft;
};
//
// 下面两个函数的实现定义在别处
//
void DrawSquare(struct Square*)
void DrawCircle(struct Circle*);
typedef struct Shape *ShapePointer;
void DrawAllShapes(ShapePointer list[], int n)
{
int i;
for (i=0; iitsType)
{
case square:
DrawSquare((struct Square*)s);
break;
case circle:
DrawCircle((struct Circle*)s);
break;
}
}
}
上面的代码不符合open close法则,因为新加入其它的shape如椭圆, DrawAllShapes函数就需要变化。
2. C++的实现
Listing 2
/*OOD solution to Square/Circle problem.*/
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(Set& list)
{
for (Iteratori(list); i; i++)
(*i)->Draw();
}
和上面C语言实现代码对比,显然符合open close 法则,加入新的shape, DrawAllShapes函数可保持不变,只是添加新的shape内容。
但是事实上如果有新的需求变化,DrawAllShapes也无法做到完全不变,任何模块只能是相对封闭,无法完全封闭。
例如我们有新的需求,要求绘制图形列表的时候,一种形状的图形要在另一种图形前面绘制。
解决方法,加入 顺序抽象类
Listing 3
/*Shape with ordering methods.*/
class Shape
{
public:
virtual void Draw() const = 0;
virtual bool Precedes(const Shape&) const = 0;
bool operatorDraw();
}
Listing 5
/*Ordering a Circle*/
bool Circle::Precedes(const Shape& s) const
{
if (dynamic_cast(s))
return true;
else
return false;
}
这里使用的Precedes函数,如果新加入shape需要改变,怎么样才能做到更好呢?
使用数据驱动获得封闭性,利用预先写好的table,我们将各个图形的优先顺序写入table,那么新加入shape只需要更新table加入新的shape。
Listing 6
/*Table driven type ordering mechanism*/
#include
#include
enum {false, true};
typedef int bool;
class Shape
{
public:
virtual void Draw() const = 0;
virtual bool Precedes(const Shape&) const;
bool operator 0) && (thisOrd > 0))
done = true;
}
else // table entry == 0
done = true;
}
return thisOrd < argOrd;
}
进一步扩展封闭性
故事还没有结束。我们已经设法使得Shape类层次和DrawShapes函数对于依赖于图形类型的画出顺序是封闭的。然而,如果画出顺序与图形类型无关,那么Shape派生类并不对这种顺序的变化封闭。我们似乎需要根据一个更加高层次的结构来决定画出各个shape的顺序。关于这个问题的深入彻底探讨已经超过了本文的范围;然而有兴趣的读者可能会考虑定义一个OrderedObject的抽象类,并从Shape类和OrderedObject类派生一个新的抽象类OrderedShape。
所有成员变量都应该是私有的
永远不要用全局变量
然而,有些情况下全局变量的方便性是很重要的。全局变量cout和cin就是例子。在这种情况下,如果没有破环开放―封闭(open-closed)原则,那么牺牲风格来获得这种方便性是值得的
RTTI是危险的
根据一般的经验,如果使用RTTI不会破坏开放―封闭(open-closed)原则,那么就是安全的 |
|
|
|
|
|
|