本周内容:
Composition:
一个class里面完整包含另一个class
图像表示如图
Delegation 就是 Composition by reference
里面对StringRep 通过指针引用
实现了“防火墙”,隔离作用,改变body不影响handle
虚函数是成员函数,且非static
如果某类中:一函数为虚函数,则:该函数在派生类中可能有不同的定义
pure virtual:基类中完全不定义,完全交给派生类去定义
impure virtual:虽然派生类可以去自己定义,但基类中也定义了
non-virtual:派生类不能修改
基类为CDocument,里面有OnFileOpen函数,函数里一系列函数,其中包括有Serialize函数,该函数为虚函数,未定义
创建派生类CMyDoc,里面定义Serialize函数——交给子类定义:Template Method
main里创建CMyDoc对象,调用父类函数OnFileOpen,执行到里面的Serialize时,转向执行子类中定义的 虚函数
类之间的三大关系、继承(inheritance)、复合(composition)、,委托(delegation)
三种关系中,最简单的是复合,通俗来讲,就是has-a关系,在一个类里面有另一个类的对象。而委托类似于复合,在一个类中有另一个类的指针;稍复杂写的事继承,由继承,继而实现多态等等。
三大关系单独来看,基本用法都不难。但是,当我们通过继承,复合,委托所构成的类之间复杂的关系,来体会他们的用法,才发现其中的精妙。
侯老师通过设计模式的实例向我们展示了类与类之间精妙的设计,设计模式,侯老师讲起来好似轻描淡写很容易,但下来仔细体会却发现并非如此。学习是一个迭代的过程,这次笔记时并不能完全理解这几个设计模式的思想,但相信之后再学习的时候能有更深入的理解。
Adapter 模式
应用背景:假设我们有正在写的程序已经设计好了接口,我们想用第三方库来开发,但是我们程序中的接口与第三方提供的接口不一致。
这种情况下,我们不想修改自己的接口,更不可能去修改第三方早就写好的接口,这时候我们就需要一个中介--适配器。
进行这样的转换的设计,成为Adapter模式,结构图如下:
在我们现实生活中,也有很多适配器的例子,例如我们出国需要一个插孔的转换器,以便能给我们的电器充电。我们就借用这个例子来说明一下吧。
class Fsocket {
public:
void Fele() {
cout << "为外国电器充电" << endl;
}
~Fsocket() {}
};
首先,我们有一个国外的插座Fsocket,能提供“充电”这一服务
class Csocket
{
public:
virtual void ele() {
cout << "为中国电器充电" << endl;
}
virtual ~Csocket() {}
protected:
private:
};
然而,我们的中国电器只能用中国的插座(调用ele函数)来充电。我们需要的是为用户提供一个中国的插座Csocket,现在我们就需要一个将Fsocket转换为Csocket的Adapter:
class Adapter :public Csocket {//继承于Csocket
private:
Fsocket F;//内含一个Fsocket对象
public:
Adapter(const Fsocket& f) :F(f) {}
virtual void ele() {
F.Fele();
}
};
我们可以看到,Adapter内有一个Fsocket对象(这就是我们所说的复合关系),而Adapter继承于Csocket,当用户调用ele时,Adapter调用Fsocket的Fele供电,这样,我们就实现了接口的转换,在使用时,我们这样使用:
int main(int argc, char* argv[])
{
Fsocket f;
Csocket* a = new Adapter(f);//用现有的Fsocket去初始化一个Adapter
//但在用户看起来这是一个Csocket,可以为“中国电器充电”
a->ele(); //然后就可以使用“Csocket”的ele了
//但实际内部是一个Fsocket
return 0;
}
这样,通过Adapter,调用了Adaptee的功能,然而用户实际在使用的时候实际用的是我们所提供的接口,并不知道实际上我们使用的事第三方库的功能。
在Head First Disign Patterns 中,作者用幽默的例子向我们展现了上面所展现的关系:If it walks like a duck and quacks like a duck,then itmustmight be aduckturkey wrapped with a duck adapter...(如果它走起来像只鸭子,叫起来像只鸭子,那么他必定可能是一直鸭子包装了鸭子适配器的火鸡)
在 Adapter 模式的模式中,我们需要注意接口继承和实现继承的区别和联系。接口继承和实现继承是面向对象领域的两个重要的概念,接口继承指的是通过继承,子类获得了父类的接口,而实现继承指的是通过继承子类获得了父类的实现(并不统共接口)。Adapter模式中Adapter既继承了父类Target的接口,却又可继承Adaptee的实现(如果上面的例子不是用复合来实现而是用多重继承来实现的话,当然,这也只可能是在C++平台下),让我们细心体会这两个概念以及设计的理念。
在视频中,侯老师用标准库中queue的例子来说明,在queue中,queue复合了一个deque对象,然后功能完全用该deque对象的操作函数来完成,这可以说是一个Adapter模式的一个特例,我的理解,queue本身就是一个adapter,又是一个target提供给用户使用,并且这个adapter的作用其实是缩小deque的功能范围,提供部分接口给用户使用。
Adapter模式适用情况主要在接口不同的时候,所以我们在平时写程序时不能够滥用,设计模式除了理解它设计的思想之外还有一个难点,就在于你需要有能力判断在什么时候用才合适,这需要我们在充分理解的基础上进行实践练习的体会。
网友评论