GeekBand C++面向对象高级编程(上)第三周

本周内容:

GeekBand C++面向对象高级编程(上)第三周_第1张图片

Composition:


GeekBand C++面向对象高级编程(上)第三周_第2张图片

一个class里面完整包含另一个class

图像表示如图


GeekBand C++面向对象高级编程(上)第三周_第3张图片


Delegation 就是 Composition by reference

GeekBand C++面向对象高级编程(上)第三周_第4张图片

里面对StringRep 通过指针引用

实现了“防火墙”,隔离作用,改变body不影响handle


GeekBand C++面向对象高级编程(上)第三周_第5张图片


GeekBand C++面向对象高级编程(上)第三周_第6张图片


GeekBand C++面向对象高级编程(上)第三周_第7张图片

虚函数是成员函数,且非static

如果某类中:一函数为虚函数,则:该函数在派生类中可能有不同的定义

pure virtual:基类中完全不定义完全交给派生类去定义

impure virtual:虽然派生类可以去自己定义,但基类中也定义了

non-virtual:派生类不能修改


GeekBand C++面向对象高级编程(上)第三周_第8张图片

基类为CDocument,里面有OnFileOpen函数,函数里一系列函数,其中包括有Serialize函数,该函数为虚函数,未定义

创建派生类CMyDoc,里面定义Serialize函数——交给子类定义:Template Method

main里创建CMyDoc对象,调用父类函数OnFileOpen,执行到里面的Serialize时,转向执行子类中定义的 虚函数


GeekBand C++面向对象高级编程(上)第三周_第9张图片


GeekBand C++面向对象高级编程(上)第三周_第10张图片


GeekBand C++面向对象高级编程(上)第三周_第11张图片

类之间的三大关系、继承(inheritance)、复合(composition)、,委托(delegation)

三种关系中,最简单的是复合,通俗来讲,就是has-a关系,在一个类里面有另一个类的对象。而委托类似于复合,在一个类中有另一个类的指针;稍复杂写的事继承,由继承,继而实现多态等等。

三大关系单独来看,基本用法都不难。但是,当我们通过继承,复合,委托所构成的类之间复杂的关系,来体会他们的用法,才发现其中的精妙。

侯老师通过设计模式的实例向我们展示了类与类之间精妙的设计,设计模式,侯老师讲起来好似轻描淡写很容易,但下来仔细体会却发现并非如此。学习是一个迭代的过程,这次笔记时并不能完全理解这几个设计模式的思想,但相信之后再学习的时候能有更深入的理解。

Adapter 模式

应用背景:假设我们有正在写的程序已经设计好了接口,我们想用第三方库来开发,但是我们程序中的接口与第三方提供的接口不一致。

这种情况下,我们不想修改自己的接口,更不可能去修改第三方早就写好的接口,这时候我们就需要一个中介--适配器。

进行这样的转换的设计,成为Adapter模式,结构图如下:

GeekBand C++面向对象高级编程(上)第三周_第12张图片

在我们现实生活中,也有很多适配器的例子,例如我们出国需要一个插孔的转换器,以便能给我们的电器充电。我们就借用这个例子来说明一下吧。

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模式适用情况主要在接口不同的时候,所以我们在平时写程序时不能够滥用,设计模式除了理解它设计的思想之外还有一个难点,就在于你需要有能力判断在什么时候用才合适,这需要我们在充分理解的基础上进行实践练习的体会。

你可能感兴趣的:(GeekBand C++面向对象高级编程(上)第三周)