一、开放封闭原则示例:
一个图形绘制函数,要求能够针对输入的不同对象,调用不同的绘制函数,如能够绘制矩形,圆形,调用相应的绘制函数。
1. 用c语言实现,这个例子其实给出了c语言模拟c++类继承的方法。利用指针的强制转换,因为指针仅仅是地址可以指向任何对象,利
用指针强制转换,告诉编译器具体按什么对象处理指针所指。
Listing 1
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; i<n; i++)
{
struct Shape* s = list[i];
switch (s->itsType)
{
case square:
DrawSquare((struct Square*)s);
break;
case circle:
DrawCircle((struct Circle*)s);
break;
}
}
}
这段代码用C模拟的C++的对象设计。
上面的代码不符合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<Shape*>& list)
{
for (Iterator<Shape*>i(list); i; i++)
(*i)->Draw();
}
和上面C语言实现代码对比,显然符合open close 法则,加入新的shape, DrawAllShapes函数可保持不变,只是添加新的shape内容。
二、里氏代换原则示例:
为了说明,我们先用第一种方法来看一个例子,第二种办法在另外一个原则中说明.
我们就看那个著名的长方形和正方形的例子。对于长方形的类,如果它的长宽相等,那么它就是一个正方形,因此,长方形类的对象中有一些正方形的对象。对于一个正方形的类,它的方法有setSide和getSide,它不是长方形的子类,和长方形也不会符合LSP。
eg:
长方形类:
public class Rectangle{
...
setWidth(int width){
this.width=width;
}
setHeight(int height){
this.height=height
}
}
正方形类:
public class Square{
...
setWidth(int width){
this.width=width;
this. height=width;
}
setHeight(int height){
this.setWidth(height);
}
}
例子中改变边长的函数:
public void resize(Rectangle r){
while(r.getHeight()<r.getWidth){
r.setHeight(r.getWidth+1);
}
}
那么,如果让正方形当做是长方形的子类,会出现什么情况呢?我们让正方形从长方形继承,然后在它的内部设置width等于height,这样,只要width或者height被赋值,那么width和height会被同时赋值,这样就保证了正方形类中width和height总是相等的.现在我们假设有个客户类,其中有个方法,规则是这样的,测试传入的长方形的宽度是否大于高度,如果满足就停止下来,否则就增加宽度的值。现在我们来看,如果传入的是基类长方形,这个运行的很好。根据LSP,我们把基类替换成它的子类,结果应该也是一样的,但是因为正方形类的width和height会同时赋值,这个方法没有结束的时候,条件总是不满足,也就是说,替换成子类后,程序的行为发生了变化,它不满足LSP。
那么我们用第一种方案进行重构,我们构造一个抽象的四边形类,把长方形和正方形共同的行为放到这个四边形类里面,让长方形和正方形都是它的子类,问题就OK了。对于长方形和正方形,取width和height是它们共同的行为,但是给width和height赋值,两者行为不同,因此,这个抽象的四边形的类只有取值方法,没有赋值方法。上面的例子中那个方法只会适用于不同的子类,LSP也就不会被破坏。
在进行设计的时候,我们尽量从抽象类继承,而不是从具体类继承。如果从继承等级树来看,所有叶子节点应当是具体类,而所有的树枝节点应当是抽象类或者接口。当然这个只是一个一般性的指导原则。使用的时候还要具体情况具体分析.