C++设计模式

978-7-115-60311-1
作者: 李阳阳
译者:
编辑: 张涛
分类: C++

图书目录:

详情

本书通过浅显易懂的例子来讲解设计模式的知识:首先,介绍设计模式的概念,在什么情况、什么场合下要用哪一种设计模式;然后,通过每一种设计模式的UML类图,明确各个小故事里讲解的设计模式中的变量之间的关系,帮助读者理解代码实现的步骤;最后,讲解设计模式之间的联系和区别。 本书案例丰富,实用性强,适合有一定C++基础的读者、求职的本科生或研究生、需要学习架构和重构架构知识的程序员阅读,也可以作为大专院校相关专业师生的参考书和培训学校的教材。

图书摘要

版权信息

书名:C++设计模式

ISBN:978-7-115-60311-1

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

版  权

著    李阳阳

责任编辑 张 涛

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315

内 容 提 要

本书通过浅显易懂的例子来讲解设计模式的知识:首先,介绍设计模式的概念,在什么情况、什么场合下要用哪一种设计模式;然后,通过每一种设计模式的UML类图,明确各个小故事里讲解的设计模式中的变量之间的关系,帮助读者理解代码实现的步骤;最后,讲解设计模式之间的联系和区别。

本书案例丰富,实用性强,适合有一定C++基础的读者、求职的本科生或研究生、需要学习架构和重构架构知识的程序员阅读,也可以作为大专院校相关专业师生的参考书和培训学校的教材。

前  言

为什么要写本书

我硕士毕业参加工作后,回顾自己求职的历程,求职路上通过的一道道“关卡”历历在目。例如,在应聘华为公司的职位时,面试官问了我工厂模式的问题;在应聘百度公司的职位时,面试官让我写策略模式的范例;在应聘字节跳动公司的职位时,面试官挑出单例模式的问题让我回答……我当时仅对常见的设计模式比较熟悉,被面试官问到陌生的设计模式及设计原则时,由于缺乏相应知识,没能当场回答出来,以至于错失许多好的机会。

目前,国内基于C++编写的设计模式的图书较少。但是,不管是校园招聘还是社会招聘,与C++相关的岗位都很多,例如SLAM算法工程师、C++开发工程师、游戏开发工程师等。

从我的面试经历来看,凡是与C++相关的岗位的面试,设计模式的考核是必不可少的。求职者若熟悉设计模式,在参加校园招聘面试时会更加从容,这也是一个加分项;在参加社会招聘面试时,求职者若理解并能够运用设计模式,会更加受招聘单位青睐。

因此,为了帮助更多人学习设计模式,我基于市场调研和自己的体会,在工作之余写了一本基于C++介绍设计模式的书。目的是:一方面可以帮助读者学习设计模式的知识;另一方面可以帮助更多求职者,尤其是参加校园招聘和社会招聘的求职者,让他们理解并能够运用设计模式,从而顺利获得心仪的Offer(职位)。

面向的读者

本书面向的读者:

(1)有一定C++基础的读者;

(2)准备参加校园招聘或者社会招聘的本科生或研究生;

(3)需要学习架构和重构架构知识的软件开发人员或算法工程人员;

(4)大专院校相关专业师生;

(5)培训学校的师生。

本书特色

本书主要有以下特色。

本书基于C++介绍设计模式,对应聘与C++相关的职位的读者来说,通过本书能快速学习设计模式。

在讲解设计模式之前,我会先介绍相关理论知识,再通过趣味性强的故事引出后续代码,代码实现部分会分步骤将故事中对应的设计模式一一展开,“手把手”教读者掌握设计模式的知识。

如何高效利用本书

本书所讲的例子浅显易懂,为了使读者高效学习本书内容,特给出如下学习建议。

首先,读者应了解每一个设计模式的概念。在什么情况、什么场合下用哪一种设计模式?没有应用设计模式的代码是什么样子,用了之后又是什么样子?设计模式带来的好处是什么?带着这些疑问阅读本书,读者对设计模式的理解会更加深刻。

然后,读者可以画出每一种设计模式的UML类图,了解各个趣味故事中讲解的设计模式中变量之间的关系,对设计模式代码实现的步骤能够做到心中有数。

最后,读者要能够将设计模式之间的联系和区别提炼出来,针对每种代码至少可以运用两种设计模式实现,并且能够说明每一种实现的目的。

本书约定

小码路与大不点是本书故事中的主人公,本书所有的故事均围绕这两位主人公展开。

思而不罔——本部分归纳C++设计模式的关键技术,并拓展设计模式的内容,便于读者加深理解。

温故而知新——本部分总结相应章的重点内容,以及通过问题引出下一部分的内容,读者带着问题去学习会更加高效。

联系作者

由于笔者水平有限,书中难免存在不足之处,恳请读者批评指正,以便笔者完善本书。

读者可通过以下方式联系到笔者。

邮箱:lyy3690@126.com。

微信公众号:码出名企路。

知乎和哔哩哔哩账号:码出名企路。

感谢

“站在巨人的肩膀上,你可以看得更远”,软件设计模式自被提出至今,经过众多前辈的总结和补充。在前辈知识的基础上,我结合自身工作经历,历时3年完成了本书,在此要感谢太多太多的人。

入职小米公司以来,我经历了许多的项目,在开发项目期间得到了许多同事的帮助,感谢我的组长、组内的每一位同事。

感谢我的研究生导师,他带我走进了计算机的世界,他使我领悟到:代码可以改变生活,科技可以使人更加幸福。

感谢张涛,他是我成长路上的指路人,在他的鼓励和帮助下,我成功完成了本书,遇见他是我的幸运。感谢默默付出的人民邮电出版社的编辑们,你们的共同努力和辛苦审核使本书得以顺利出版。

最后,我还要感谢我的亲人、朋友的陪伴和鼓励,祝愿他们永远健康、幸福。

笔者

第1章 理论基础

设计模式(Design Pattern)是对设计经验的显式表示。每个设计模式描述了一个反复出现的问题及其解法的核心内容,它命名、抽象并标识了一个通用设计结构的关键部分,使之可用来创建一个可复用的设计。程序员使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码的可靠性和程序的重用性。

在介绍设计模式之前,本章先介绍C++的核心——类的设计。本章主要讲解类的构造和UML类图的组成,引用高焕堂老师提出的EIT(Engine Interface Tire)造型,最后通过EIT造型拼接出设计模式,为读者进行设计模式的学习奠定基础。

1.1 类方法

本章的内容从小码路买的第一辆汽车DZ说起。DZ由引擎提供动力,假设引擎是不会坏的。汽车行驶两年后,轮胎轻微变形,这时小码路想给汽车换一套新的轮胎,于是一个汽车类就产生了。

//汽车类
class Car
{
    public:
        Car(string en):engineName(en){}
        void SetCommonEngine(){cout<<"commonEngine is: "<< engineName<<endl;}
        virtual string SetDiffTire(string tire) = 0;
    protected:
        string engineName;
};

DZ的原装“miqilin”轮胎质量相当好。可是小码路买了车之后,生活拮据,所以准备换相对便宜的“weichai”轮胎。小码路考虑到两年后又要为DZ换轮胎,所以上面程序中提供的轮胎接口SetDiffTire(string tire)就显得相当重要了,改写后的程序如下。

//DZ继承自汽车类
class DZ:public Car
{
    public:
        DZ(string en):Car(en){}
        string SetDiffTire(string tire)
        {
            return tire;
        }    
};
 
Car *car = new DZ("weichai");
 
car->SetCommonEngine();
 
cout<<car->SetDiffTire("miqilin")<<endl;
cout<<car->SetDiffTire("weichai")<<endl;
delete car;

1.2 类间关系

类图表示的是类与类之间的关系,此种关系通常用UML(Unified Modeling Language,统一建模语言)表示。类图在软件设计及应用架构前期设计中是不可或缺的一部分,它的主要组成部分包括类名、类方法(也叫成员方法、成员函数)和成员变量,其中类方法包含返回值类型,成员变量包含数据类型,这些组成部分由一个矩形框包围起来。一个UML类图的组成部分的完整表达式如下。

[是否可见]  [成员变量名称/类方法名称]:[数据类型/返回值类型] [ = 默认值(可选)]

其中部分符号的含义如下。

+:可见(public)。

−:自身可见(private)。

#:继承可见(protected)。

因此前面提到的Car类的UML类图如图1-1所示。

▲图1-1 Car类的UML类图

图1-1完整表示了一个UML类图的组成,汽车类名称Car正上居中;虚接口SetDiffTire()返回string类型参数;公有成员函数SetCommonEngine()无返回值;Car(string en)为自身构造函数;公有成员变量engineName的类型是string。

在软件设计或架构设计中,类通常不是单独存在的,如上文提到的DZ类与Car类存在继承关系,这里说的继承是一种泛指的关系。之所以说是泛指关系,是因为类之间的关系根据耦合度由强到弱又分为接口实现关系、继承泛化关系、不可分离组合关系、可分离聚合关系、关联关系和依赖关系。下面分别阐述各个类间关系的UML类图的表示方式。

1.2.1 接口实现关系

接口实现关系就是派生类必须重写接口中的所有方法,在UML类图中用“虚线+空心箭头”表示,其中箭头指向基类。

例如,不同品牌计算机的售价不同,用UML类图表示的小米笔记本电脑和华为笔记本电脑各自实现的笔记本电脑类的售价接口如图1-2所示。

▲图1-2 接口实现关系

图1-2说明了接口实现关系的UML类图的组成,基类笔记本电脑Computer包含一个笔记本电脑售价的公有虚方法SalePrice(Computer*sonCom);小米笔记本电脑类XiaoMiComputer和华为笔记本电脑类HuaWeiComputer均继承基类Computer,并实现各自的具体售价接口SalePrice(Computer *sonCom),返回int类型的笔记本电脑售价参数,完成接口的实现。

注:函数有时也称为方法。

1.2.2 继承泛化关系

继承泛化关系就是常说的继承关系,派生类继承基类,基类被看作“一般设计”,派生类被看作“特殊设计”,因此继承泛化关系也被看作一般与特殊的关系,在UML类图中用“实线+实心箭头”表示,其中箭头指向基类。

例如,动物类会走路、吃东西和发声,猫类和狗类继承动物类,猫类会爬树而狗类会看门,并且它们发声的方式也不一样。用UML类图表示的动物类、猫类和狗类的关系如图1-3所示。

▲图1-3 继承泛化关系

图1-3说明了继承泛化关系的UML类图的组成,基类动物类Animal含有保护(protected)类型的成员变量animalName和animalColor,走路方法OnFoot()和吃东西方法Eat()是动物的共性,不同的发声方式Say()被定义为虚接口。派生类猫类Cat和狗类Dog继承自动物类Animal,实现具体的Say()方法;并且Cat类有自身独有的爬树方法OnTree(),Dog类有自身独有的看门方法LookDoor(),这样就完成了继承并且实现了扩展的功能。

1.2.3 不可分离组合关系

不可分离组合关系可以用整体和部分之间的关系来解释,部分是不能脱离整体单独存在的。部分对象与整体对象是不可分离的,一旦整体对象析构,部分对象就会随之消失,它们属于同一个生命周期。不可分离组合关系在UML类图中用“实线+实心菱形”表示,其中实心菱形指向整体。

例如,一个人的身体包含脚部、手部和头部等,各个部分实现各自的功能,但各个部分又不能脱离身体而单独存在,用UML类图表示的身体类、脚部类、头部类、手部类的关系如图1-4所示。

▲图1-4 不可分离组合关系

图1-4说明了不可分离组合关系的UML类图的组成,整体身体类Body由私有成员变量头部类对象Head、脚部类对象Feet、手部类对象Hand和私有成员方法ComBody()组成;脚部类Feet实现走路方法Onfoot(),头部类Head实现吃饭方法Eat()和观察世界方法See(),手部类Hand实现操作方法OnHand()。各个部分对象不能独立于整体对象存在,部分与整体是一种不可分离的组合关系。

注:类对象是类的实例。

1.2.4 可分离聚合关系

可分离聚合关系也可以说成是整体与部分的关系,它与不可分离组合关系的区别是,这种整体与部分是可以分离的,也就是说部分是可以脱离整体单独存在的。UML类图中用“实线+空心菱形”表示这种关系,其中空心菱形指向整体。

例如,一所学校中有教师和学生,教师和学生都是可以作为个体存在的,用UML类图表示的学校类、教师类、学生类的关系如图1-5所示。

图1-5说明了可分离聚合关系的UML类图的组成,学校类School包含私有成员变量教师类对象集合set<Teacher>、学生类对象集合list<Student>,公有成员方法招聘教师方法RecruitedTeacher (set<Teacher>)、学生考试方法ExamStudent(list<Student>)等。教师类Teacher含有教书方法Teach(),学生类Student含有学习方法Study()和玩耍方法Play()。值得注意的是,部分是可以单独存在的,部分可以脱离整体,这种关系为可分离聚合关系。

▲图1-5 可分离聚合关系

1.2.5 关联关系

关联关系顾名思义就是一个类与另一个类在对象之间的联系,联系可以是双向的,也可以是单向的。在UML类图中,双向关联关系用没有箭头的实线表示,单向关联关系用“实线+箭头”表示,箭头指向被关联的类。

例如,医生与病人之间的关系,个体与自身手机号、身份证号之间的关系。在代码中,将一个类的对象作为另一个类的成员变量来达到两者关联的目的。

其中,医生与病人双向关联关系的UML类图如图1-6所示。

▲图1-6 双向关联关系

图1-6说明了双向关联关系的UML类图的组成,医生类Doctor和病人类Patient为双向关联,医生类Doctor包含私有成员变量病人类对象集合list<Patient>、医生姓名doctorName和公有类方法医生诊断方法Diagnosis();病人类Patient包含私有成员变量医生类对象集合list<Doctor>、病人姓名patientName和公有类方法病人看病方法SeeADoctor();Doctor类和Patient类分别包含对方的类对象作为成员变量,从而实现双向关联关系。

其中,个体与手机号、身份证号单向关联关系的UML类图如图1-7所示。

图1-7说明了单向关联关系的UML类图的组成,个体类People包含手机号类Phone和身份证号类Identity这两个私有成员变量;People类实现个体标志方法IdPeople(),Phone类实现设定手机号方法SetPhoneNum(int* pn),Identity类实现设定身份证号方法SetIdNum(int* in);People类指向Phone类和Identity类,实现了一种单向的关联关系。

▲图1-7 单向关联关系

1.2.6 依赖关系

只有在一个类依赖另一个类中的方法时才存在依赖关系,一般将类作为参数传递,通过对方法的调用实现一个类访问另一个类的功能。在UML类图中,使用带箭头的虚线表示这类关系,箭头指向被依赖的类。

例如,同事之间通过邮件进行工作交流,用UML类图表示的同事类、邮件类的关系如图1-8所示。

▲图1-8 依赖关系

图1-8说明了依赖关系的UML类图的组成,同事类Colleague由私有成员变量同事名称collName、同事工号numId和工作方法Work(Mail mail)组成,其中Work(Mail mail)中的形参是依赖关系实现的关键;邮件类Mail包含私有成员变量邮件主题mailTopic和发送时间sendTime,并且实现发送消息方法TransferMessage();Colleague类中的Work(Mail mail)方法完成对TransferMessage()的调用,Colleague类只有依赖Mail类才能工作。

1.3 EIT造型

EIT造型是高焕堂老师在讲解Android架构时提到的一种用于表述类与类之间关系纽带的概念,这种纽带把本无关系的单个类变成了联系密切的“亲戚”。

1.3.1 EIT是什么

从类方法与UML类图中可以抽离出一个标准模板,暂且称这个标准模板为公式。只要有公式,就能很好地理解自变量与因变量之间的关系。

例如,力矩与力臂的关系公式FL,可以理解为力臂(L)一定的时候,力(F)越大,力矩(M)越大;又如,力与加速度的关系公式= ma,可以理解为物体加速度(a)跟作用力(F)成正比,跟物体的质量(m)成反比,且与物体质量的倒数成正比。

把这种自变量与因变量之间的关系应用于软件设计,是否也能找到一种类似的“公式”,以便我们理解代码与框架之间是如何“沟通”的呢?答案是肯定的,这就是高焕堂老师讲解Android架构时提出的EIT造型,这种造型也是代码设计时用的一种标准“公式”。

EIT造型由以下3部分组成。

E : Engine,即引擎,基类。I : Interface,即接口。T : Tire,即轮胎,派生类。

引擎通过接口驱动轮胎带动整辆车往前行驶,EIT造型的形象表示如图1-9所示。

▲图1-9 EIT造型

图1-9形象地描述了EIT造型的3种组成部分之间的关系,中间的接口I用以联系引擎E和轮胎T,应用在软件设计中,则是基类E和派生类T之间的联系通过接口I来实现。

1.3.2 程序应用

EIT造型迁移到编程实践中,E是基类、I是接口、T是派生类,应用到1.1节的Car类的案例中,Car是E、SetDiffTire(string tire)是I、DZ是T,用UML类图表示的EIT造型如图1-10所示。

图1-10说明了EIT造型的程序应用,基类Car是EIT造型中的E,基类中的虚方法SetDiffTire (string tire)是EIT造型中的I,派生类DZ是EIT造型中的T,派生类实现具体的SetDiffTire(string tire)(接口I)。

▲图1-10 EIT造型的程序应用

图1-10中,Car与DZ紧耦合,在软件设计中可以继续优化,将Car与DZ分离(解耦),增加一个接口类SetTireInterface,在Car类中实现安装不同轮胎的方法DiffTire();各个派生类继承接口类,例如,DZInterface派生类继承自接口类SetTireInterface,DZInterface派生类“安装”符合自身应用需求的Tire,具体代码如下。

//新增接口类
class SetTireInterface
{
    public:
        SetTireInterface(string tn):m_tireName(tn){}
        virtual string SetDiffTire() = 0;
    protected:
        string m_tireName;
};
//新增接口派生类
class DZInterface:public SetTireInterface
{
    public:
        DZInterface(string tn):SetTireInterface
        (tn){}
        string SetDiffTire()
        {
            return m_tireName;
        }    
};
//Car与DZ解耦
class Car
{
    public:
        Car(string en):engineName(en)
        {
            m_Interface = new DZInterface("miqilin");
        }
        void SetCommonEngine()
        {
            cout<<"commonEngine is: "<< engineName<<endl;
        }
        void DiffTire()
        {   
           
            cout<<m_Interface->SetDiffTire()<<endl;
        }
    protected:
        string engineName;
        SetTireInterface* m_Interface;
};
//客户端主程序
int main()
{
    Car *car = new Car("weichai");
 
    car->SetCommonEngine();
    car->DiffTire();
    delete car;
}

1.3.3 优化设计

1.3.2小节中对最初的Car类进行了设计,并且完成了对应的代码,根据1.3.2小节代码中的各个类的组成及类间关系,绘制优化设计后的EIT造型的UML类图,如图1-11所示。

▲图1-11 优化后的EIT造型

与图1-10相比,优化后的EIT造型多了一个接口类对象SetTireInterface(EIT中的I),这个接口类对象替代了原来的接口方法SetDiffTire(string tire);EIT中的E和T保持不变,并且在E的Car对象中包含接口类对象SetTireInterface的成员变量,Car对象中的接口类对象SetTireInterface在构造Car对象的同时被赋值为m_Interface,并且在调用Car对象的DiffTire()方法中完成对SetDiffTire()的间接控制。

但是,这个优化后的SetTireInterface与DZInterface仍然存在继承泛化关系,能否将两者改成关联关系呢?这里考虑将接口及派生类分离成两个独立类,整个流程代码完善后的结果如下。

#include <iostream>
#include <cstring>
#include <algorithm>
 
using namespace std;
 
class SetTireInterface;
//派生类DZ对象
class DZ
{
    public:
        DZ(){}
        void SetDiffTire(string tire)
        {
            cout<<"setDiffTire is: "<<tire<<endl;;
        }
};
//派生类对象独立于接口类
class SetTireInterface
{
    public:
        SetTireInterface()
        {
            m_dzInterface = new DZ();
        }
        void EnableSetDiffTire(string tire)
        {
            m_dzInterface->SetDiffTire(tire);
        }
    protected:
        DZ* m_dzInterface;
};
//Car类与接口单向关联
class Car
{
    public:
        Car(string en):engineName(en)
        {
            m_Interface = new SetTireInterface();
        }
        void SetCommonEngine()
        {
            cout<<"commonEngine is: "<< engineName<<endl;
        }
        void DiffTire(string tire)
        {   
           
            m_Interface->EnableSetDiffTire(tire);
        }
    protected:
        string engineName;
        SetTireInterface* m_Interface;
};
//客户端主程序
int main()
{
    Car *car = new Car("weichai");
 
    car->SetCommonEngine();
    car->DiffTire("miqilin");
    delete car;
}

去除继承泛化关系后的优化方案的代码如上,根据以上代码绘制的UML类图如图1-12所示。

图1-12说明了再次优化后由继承泛化关系变成关联关系的EIT造型的组成,Car对象与SetTireInterface对象为单向关联关系,Car对象包含SetTireInterface对象的成员变量,成员函数与图1-11保持一致;DZ对象与SetTireInterface对象为双向关联关系,DZ对象包含SetTireInterface对象的成员变量,SetTireInterface对象包含DZ对象的成员变量,SetTireInterface对象的成员方法EnableSetTireInterface(string tire)中完成对SetDiffTire(string tire)的关联控制。

图1-12中,Car、SetTireInterface、DZ之间的关联替代图1-11中SetTireInterface与DZInterface之间的继承泛化,使SetTireInterface与DZ实现解耦,方便后续开发者对程序进行扩展和维护,实现可靠、完美的软件设计。

▲图1-12 关联关系的EIT造型

1.4 组合设计模式

1.1节中讲解的类方法表明了类内的关系,1.2节中讲解的UML类图描述了类与类之间的关系,将其抽象成一个固定公式,即1.3节中讲解的EIT造型。EIT造型通过对UML类图中各个关系的排列组合与应用,实现软件基本架构的搭建与设计。

软件架构设计的基础就是抽离出公共部分进行复用,作为E;最主要的工作就是设计出接口,作为I;最后设计出可更换的T。两个或两个以上EIT造型组合就成了组合设计模式。从图1-13可以清晰地看出类与类、类与EIT造型、EIT造型与EIT造型等之间的进阶关系。

▲图1-13 类、UML类图、EIT造型
之间的关系

图1-13说明了类、UML类图、EIT造型之间的关系,类对象与类对象之间是通过UML类图进行组合、继承、关联或依赖的,类对象加上接口方法构成了EIT造型,各个EIT造型之间的排列组合构成了组合设计模式。将这种类、UML类图、EIT造型之间按层次递进的关系呈现出来,可使读者更容易理解组合设计模式的由来和组成,这三者是设计模式理论基础的核心,介绍它们之间的关系可为组合设计模式的展开说明奠定基础。

将组合设计模式的思想应用到1.1节介绍的类方法中,Car类与DZ类构成一个EIT1造型,其中SetDiffTire()传参改为Car类的成员变量,代码设计如下。

#include <iostream>
#include <cstring>
#include <algorithm>
 
using namespace std;
//EIT1造型:Car、SetDiffTire()和DZ
class Car
{
    public:
        Car(){}
        Car(string en):engineName(en){}
        void SetCommonEngine(){cout<<"commonEngine is: "<< engineName<<endl;}
        virtual string SetDiffTire() = 0;
    public:
        string engineName;
        string tireName;
};
 
class DZ:public Car
{
    public:
        DZ():Car(){}
        DZ(string en):Car(en){}
        string SetDiffTire()
        {
            return tireName;
        }    
};

不同种类的汽车轮胎由固定的工厂进行加工制造,例如,“miqilin”轮胎有自己的“miqilin”工厂,工厂加工出轮胎后,应用在小码路购买的DZ品牌汽车上。同理,轮胎工厂和“miqilin”轮胎工厂组成一个EIT2造型,代码设计如下。

//EIT2造型 CreateCarTire、ReturnCarName()和 MQLTireFactory
class CreateCarTire
{
    public:
        Car* ProductCar()
        {
            return ReturnCarName();
        }
        virtual Car* ReturnCarName()=0;
};
 
class MQLTireFactory:public CreateCarTire
{
    public:
        Car* ReturnCarName()
        {
           Car *car = new DZ();
           return car;
        }
};

如图1-13所示,将不同的EIT造型组合在一起可以得到软件的基本架构,本案例中将EIT1造型与EIT2造型关联在一起,EIT1造型通过构造Car对象,调用SetCommonEngine()方法输出轮胎类型,EIT2造型通过构造CreateCarTire对象,设定CreateCarTire的轮胎名称tireName,最终调用SetDiffTire()方法实现“DZ使用miqilin轮胎”的过程,客户端主程序如下。

//客户端主程序
int main()
{
    Car *car = new DZ("weichai");
 
    car->SetCommonEngine();
    
    CreateCarTire* createCarTire = new MQLTireFactory();
    Car *carSetTire = createCarTire->ProductCar();
    carSetTire->tireName = "miqilin";
    cout<<carSetTire->SetDiffTire()<<endl;
    delete car;
}

根据以上设计流程,将两个或两个以上的EIT造型以不同的方式组合在一起,形成了组合设计模式。EIT1造型和EIT2造型组合构成的设计模式的UML类图如图1-14所示。

▲图1-14 EIT造型组合构成的设计模式的UML类图

图1-14说明了EIT造型组合构成组合设计模式的过程,EIT1造型由类对象Car、接口方法SetDiffTire()、类对象DZ组成,Car类包含公有成员变量引擎名称engineName和轮胎名称tireName、两个类构造函数Car()和Car(string en)、公有的引擎方法SetCommonEngine(),DZ类实现具体的SetDiffTire()方法;EIT2造型由类对象CreateCarTire、接口方法ReturnCarName()、类对象MQLTireFactory组成,CreateCarTire类包含公有成员生产汽车方法ProductCar(),MQLTireFactory类实现具体的ReturnCarName()。图1-14展现了组合设计模式的构成部分,是后续讲解设计原则和设计模式的基础和关键。

1.5 总结

本章介绍了设计模式的基本理论知识。对类方法的介绍,可以让读者回想起C++设计的核心思想是面向对象;对UML类图的详细讲解,可以让读者明白类与类之间的关系,用这些关系将类组合成EIT造型,进而设计出符合开发者或程序维护者需求的设计模式。设计模式的最终应用正是发挥面向对象优势的最佳体现,读者掌握了本章的基本知识,学习后续的设计原则和设计模式将事半功倍。

思而不罔

在UML类图中,接口实现关系和继承泛化关系是比较常用的,不可分离组合关系和可分离聚合关系是难以区分的,关联关系和依赖关系又是紧密相连的,请说出这3对关系之间的区别。

Car类的设计总共经历了3次优化,请说出每次具体优化了什么,以及为什么要进行这样的优化设计。

温故而知新

面向对象的主要特性是继承、封装与多态,这些特性都建立在类方法实现的基础上,只有通过对类方法的不断改造、类关系的不断升级,才可将面向对象的特性发挥到极致。面对同一需求,不同的开发者可能会给出不同的实现方案,如何设计一套可靠、可扩展和易维护的框架?本章的逐步优化设计给开发者提供了一个良好的参考思路。框架设计的改进其实已经体现出了设计模式应用的雏形,这是后续内容中的重点。

如何发挥面向对象的优势?如何设计一套可靠的框架?掌握六大设计原则和23种设计模式是关键。下面就正式开始软件设计模式学习之旅吧!

相关图书

CMake构建实战:项目开发卷
CMake构建实战:项目开发卷
代码审计——C/C++实践
代码审计——C/C++实践
零基础学Qt 6编程
零基础学Qt 6编程
C++现代编程技术写给工程师的C++教程
C++现代编程技术写给工程师的C++教程
C++ Templates(第2版)中文版
C++ Templates(第2版)中文版
C/C++代码调试的艺术(第2版)
C/C++代码调试的艺术(第2版)

相关文章

相关课程