面向对象的一些法则

面向对象的一些法则:
法则1:优先使用(对象)组合,而非继承
组合是一种通过创建一个组合了其它对象的对象,从而获得新功能的复用方 法有些时候也称之为“聚合”(aggregation)或“包容”containment)
聚合:一个对象拥有另一个对象或对另一个对象负责(即一个对象包含另一个对象或是另一个对象的一部分),并且聚合对象和其所有者具有相同的生命周期。
包容:一种特殊类型的组合,对于其它对象而言,容器中的被包含对象是不可见的,其它对象仅能通过容器对象来访问被包含对象。(Coad)

组合的优点和缺点:
优点:
1、容器类仅能通过被包含对象的接口来对其进行访问。
2、“黑盒”复用,因为被包含对象的内部细节对外是不可见,封装性好。
3、实现上的相互依赖性比较小。(被包含对象与容器对象之间的依赖关系比较少)
4、每一个类只专注于一项任务。
5、通过获取指向其它的具有相同类型的对象引用,可以在运行期间动态地定义(对象的)组合。
缺点:
1、从而导致系统中的对象过多。
2、为了能将多个不同的对象作为组合块(composition block)来使用,必须仔细地对接口进行定义。

继承的优点和缺点
优点:
1、容易进行新的实现,因为其大多数可继承而来。
2、易于修改或扩展那些被复用的实现。
缺点:
1、破坏了封装性,因为这会将父类的实现细节暴露给子类。
2、“白盒”复用,因为父类的内部细节对于子类而言通常是可见的。
3、当父类的实现更改时,子类也不得不会随之更改。
4、从父类继承来的实现将不能在运行期间进行改变。


法则2:针对接口编程,而非(接口的)实现

接口
接口是一个对象在对其它的对象进行调用时所知道的方法集合。
一个对象可以有多个接口(实际上,接口是对象所有方法的一个子集)
类型是对象的一个特定的接口。不同的对象可以具有相同的类型,而且一个对象可以具有多个不同的类型。
一个对象仅能通过其接口才会被其它对象所了解。
接口是实现插件化(pluggability)的关键
实现继承和接口继承实现继承( 类继承):一个对象的实现是根据另一个对象的实现来定义的。
接口继承( 子类型化):描述了一个对象可在什么时候被用来替代另一个对象。
C++的继承机制既指类继承,又指接口继承。
C++通过继承纯虚类来实现接口继承。

优点:
Client不必知道其使用对象的具体所属类。
一个对象可以很容易地被(实现了相同接口的)的另一个对象所替换。
对象间的连接不必硬绑定(hardwire)到一个具体类的对象上,因此增加了灵活性。
松散藕合(loosens coupling)。
增加了重用的可能性。提高了(对象)组合的机率,因为被包含对象可以是任何实现了一个指定接口的类。
缺点:
设计的复杂性略有增加


法则3:开放-封闭法则(OCP)
开放-封闭法则认为我们应该试图去设计出永远也不需要改变的模块
我们可以添加新代码来扩展系统的行为。我们不能对已有的代码进行修改
符合OCP的模块需满足两个标准:
可扩展,即“对扩展是开放的”(Open For Extension)-模块的行为可以被扩展,以需要满足新的需求。
不可更改,即“对更改是封闭的”(Closed for Modification)-模块的源代码是不允许进行改动的

法则4:Liskov替换法则(LSP)
Liskov替换法则(LSP)是根据“多态”而得出的,在PageManager 中有很多地方使用到这中作法:
例如:
   Public void drawAno(CUAo * pUao)
{
        pUao->Draw(dc);
}
方法drawAno应该可与CUAo类的任何子类一起工作,
若一个函数未能满足LSP,那么可能是因为它显式地引用了超类的一些或所有子类。
这样的函数也违背了OCP,因为当我们创建一个新的子类时,会不得不进行代码的修改。
为了保持LSP(并与开放-封闭法则一起),所有子类必须符合使用基类的client所期望的行为。

LSP保证一个子类总是能够被用在其基类可以出现的地方

来做个小小的测试:
看看这样的关系应该怎样确定:
长方形   正方形
如果是你你应该怎样来设计他们之间的关系呢?


class Rectangle 
{
public:
 virtual int area();
 virtual void SetHeight(const int nHeight);
 virtual void SetWidth(const int nWidth);
 Rectangle(const int nHeight = 0,const int nWidth = 0);
 virtual ~Rectangle();
protected:
 int m_nWidth;
 int m_nHeight;
};
class Square : public Rectangle
{
public:
 Square(const int nHeight = 0);
 virtual ~Square();
 virtual void SetHeight(const int nHeight);
 virtual void SetWidth(const int nWidth);
};

从结果来讲我们违背了LSP法则
一个数学意义上的正方形可能是一个四边形,但是一个Square对象不是一个Rectangle对象,因为一个Square对象的行为与一个Rectangle对象的行为是不一致的!
从行为上来说,一个Square 不是一个Rectangle!一个Square对象与一个Rectangle对象之间不具有多态的特征。

阅读更多
个人分类: 经验总结
上一篇关于class 的理解
下一篇如何将单元测试演变为功能测试(哈哈,是不是有点矛盾??)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭