0 创建型模式

这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

1 简单工厂模式

简单工厂模式并 不属于 GoF 的 23 种设计模式。

那么为什么要使用工厂模式?示例代码如下:

#include <iostream>

using namespace std;

class Fruit
{ public: Fruit(string kind)
{
this->kind = kind; if (kind == "apple")
{
//代表苹果
//苹果的初始化方式
} else if (kind == "banana")
{
//代表香蕉
//香蕉的初始化方式
}
} void getName()
{
if (this->kind == "apple")
{
cout << "我是苹果" << endl;
}
else if (this->kind == "banana")
{
cout << "我是香蕉" << endl;
}
} private: string kind;//代表水果的种类
}; int main(void)
{
//创建一个苹果
Fruit *apple = new Fruit("apple");
apple->getName();
delete apple; //main函数跟Fruit类的构造函数耦合度高, 随着水果种类的增加 构造函数越来越复杂 return 0;
}

不难看出,Fruit类是一个“巨大的”类,在该类的设计中存在如下几个问题:

  1. 在Fruit类中包含很多“if…else…”代码块,整个类的代码相当冗长,

    代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在

    还将影响系统的性能,程序在执行过程中需要做大量的条件判断。

  2. Fruit类的职责过重,它负责初始化和显示所有的水果对象,将各种水

    果对象的初始化代码和显示代码集中在一个类中实现,违反了“单一职责原

    则”,不利于类的重用和维护。

  3. 当需要增加新类型的水果时,必须修改Fruit类的源代码,违反了“开

    闭原则。

1.1 模式中的角色和职责

工厂(Factory)角色:简单工厂模式的核心,它负责实现创建所有实例

的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。

抽象产品(AbstractProduct)角色:简单工厂模式所创建的所有对象的

父类,它负责描述所有实例所共有的公共接口。

具体产品(Concrete Product)角色:简单工厂模式所创建的具体实例

对象

1.2 案例

#include <iostream>

using namespace std;

//抽象的水果类
class Fruit
{ public: virtual void getName() = 0;
}; class Apple :public Fruit
{ public: virtual void getName()
{
cout << "我是苹果" << endl;
}
}; class Banana :public Fruit
{ public: virtual void getName()
{
cout << "我是香蕉" << endl;
}
}; //添加一个新产品 梨子
class Pear :public Fruit
{ public: virtual void getName()
{
cout << "我是梨子" << endl;
}
}; //工厂
class Factory
{ public: //水果生产器
Fruit * createFruit(string kind)
{
Fruit *fruit = NULL; if (kind == "apple")
{
fruit = new Apple;
} else if(kind == "banana")
{
fruit = new Banana;
} //添加一个梨子 修改了工厂的方法,违背了开闭原则
else if (kind == "pear")
{
fruit = new Pear;
} return fruit;
}
}; int main(void)
{
//人们是跟工厂打交道
Factory *factory = new Factory; //创建一个工厂 //给我来一个苹果
Fruit *apple = factory->createFruit("apple");
apple->getName(); //香蕉
Fruit *banana = factory->createFruit("banana");
banana->getName(); //梨子
Fruit *pear = factory->createFruit("pear");
pear->getName(); delete apple;
delete banana;
delete pear; delete factory; return 0;
}

1.3 优缺点

优点:

  1. 实现了对象创建和使用的分离。

  2. 不需要记住具体类名,记住参数即可,减少使用者记忆量。

缺点:

  1. 对工厂类职责过重,一旦不能工作,系统受到影响。

  2. 增加系统中类的个数,复杂度和理解度增加。

  3. 违反“开闭原则”,添加新产品需要修改工厂逻辑,工厂越来越复杂。

1.4 适用场景

  1. 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂

    方法中的业务逻辑太过复杂。

  2. 客户端只知道传入工厂类的参数,对于如何创建对象并不关心

2 工厂模式

2.1 模式中的角色和职责

简单工厂模式 + “开闭原则” = 工厂模式

工厂模式比简单工厂模式多一个 抽象工厂角色

抽象工厂(Abstract Factory)角色:工厂方法模式的核心,任何工厂类

都必须实现这个接口。

工厂(Concrete Factory)角色:具体工厂类是抽象工厂的一个实现,

负责实例化产品对象。

抽象产品(Abstract Product)角色:工厂方法模式所创建的所有对象

的父类,它负责描述所有实例所共有的公共接口。

具体产品(Concrete Product)角色:工厂方法模式所创建的具体实例对象。

2.2 案例

示例代码:

#include <iostream>

using namespace std;

//抽象的水果类
class Fruit
{
public: virtual void getName() = 0;
}; class Apple :public Fruit
{
public: virtual void getName()
{
cout << "我是苹果" << endl;
}
}; class Banana :public Fruit
{
public: virtual void getName()
{
cout << "我是香蕉 " << endl;
}
}; //添加一个梨产品
class Pear : public Fruit
{
public: virtual void getName()
{
cout << "我是梨子 " << endl;
}
}; //抽象的工厂类
class AbstractFactory
{
public: virtual Fruit * createFruit() = 0;//抽象的水果生产器
}; //苹果的工厂
class AppleFactory :public AbstractFactory
{
public: virtual Fruit * createFruit()
{
return new Apple;
}
}; //香蕉工厂
class BananaFactory : public AbstractFactory
{
public: virtual Fruit *createFruit()
{
return new Banana;
}
}; //梨的工厂
class PearFactory :public AbstractFactory
{
public: virtual Fruit *createFruit()
{
return new Pear;
}
}; int main(void)
{ /*
根据依赖倒转原则针对接口编程,
怎么针对接口编程?
就是 只需要使用抽象工厂类的指针,和抽象水果类的指针,
通过多态的特性,就可以搞定完成具体类的业务。
*/ AbstractFactory *abFactory = NULL;
Fruit *fruit = NULL; // 抽象水果类指针,完成 Apple 业务
abFactory = new AppleFactory;
fruit = abFactory->createFruit();
fruit->getName(); delete abFactory;
delete fruit; // 抽象水果类指针,完成 Banana 业务
abFactory = new BananaFactory;
fruit = abFactory->createFruit();
fruit->getName(); delete abFactory;
delete fruit; // 抽象水果类指针,完成 Pear 业务
abFactory = new PearFactory;
fruit = abFactory->createFruit();
fruit->getName(); delete abFactory;
delete fruit; return 0;
}

运行结果:

2.3 优缺点

优点:

  1. 不需要记住具体类名,甚至连具体参数都不用记忆。

  2. 实现了对象创建和使用的分离。

  3. 系统的可扩展性也就变得非常好,无需修改接口和原类。

缺点:

  1. 增加系统中类的个数,复杂度和理解度增加。

  2. 增加了系统的抽象性和理解难度。

2.4 适用场景

  1. 客户端不知道它所需要的对象的类。

  2. 抽象工厂类通过其子类来指定创建哪个对象。

3 抽象工厂模式

由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑 将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是抽象工厂模式的基本思想。

工厂方法模式+ “产品族” = 抽象工厂方法模式

3.1 产品族与产品等级结构

3.2 模式中的角色和职责

抽象工厂(Abstract Factory)角色:它声明了一组用于创建 一族产品 的方法,每一个方法对应一种产品。

具体工厂(Concrete Factory)角色:它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。

抽象产品(Abstract Product)角色:它为 每种产品 声明接口,在抽象产品中声明了产品所具有的业务方法。

具体产品(Concrete Product)角色:它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

3.3 案例

示例代码:

#include <iostream>

using namespace std;

// 苹果抽象类,供具体产地苹果实现
class AbstractApple
{
public: virtual void getName() = 0;
}; // 香蕉抽象类,供具体产地香蕉实现
class AbstractBanana
{
public: virtual void getName() = 0;
}; // 苹果产品族
class ChinaApple : public AbstractApple
{
public: virtual void getName()
{
cout << "中国苹果" << endl;
}
}; class USAApple : public AbstractApple
{
public: virtual void getName()
{
cout << "美国苹果" << endl;
}
}; class JapanApple : public AbstractApple
{
public: virtual void getName()
{
cout << "日本苹果" << endl;
}
}; // 香蕉产品族
class ChinaBanana : public AbstractBanana
{
public: virtual void getName()
{
cout << "中国香蕉" << endl;
}
}; class USABanana : public AbstractBanana
{
public: virtual void getName()
{
cout << "美国香蕉" << endl;
}
}; class JapanBanana : public AbstractBanana
{
public: virtual void getName()
{
cout << "日本香蕉" << endl;
}
}; // 抽象的工厂类,供具体产品族的工厂实现
class AbstractFactory
{
public: virtual AbstractApple *createApple() = 0;
virtual AbstractBanana *createBanana() = 0;
}; // 中国工厂
class ChinaFactory : public AbstractFactory
{
virtual AbstractApple *createApple()
{
return new ChinaApple;
} virtual AbstractBanana *createBanana()
{
return new ChinaBanana;
}
}; // 美国工厂
class USAFactory : public AbstractFactory
{
virtual AbstractApple *createApple()
{
return new USAApple;
} virtual AbstractBanana *createBanana()
{
return new USABanana;
}
}; // 日本工厂
class JapanFactory : public AbstractFactory
{
virtual AbstractApple *createApple()
{
return new JapanApple;
} virtual AbstractBanana *createBanana()
{
return new JapanBanana;
}
}; int main(void)
{
// 要一个中国的苹果,美国的苹果,日本的香蕉 AbstractApple *apple = NULL;
AbstractBanana *banana = NULL;
AbstractFactory *factory = NULL; factory = new ChinaFactory; // 中国的苹果
apple = factory->createApple();
apple->getName(); delete apple;
delete factory; factory = new USAFactory; // 美国的苹果
apple = factory->createApple();
apple->getName(); delete apple;
delete factory; factory = new JapanFactory; // 日本的香蕉
banana = factory->createBanana();
banana->getName(); delete banana;
delete factory; return 0;
}

运行结果:

3.4 优缺点

优点:

  1. 拥有工厂方法模式的优点

  2. 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端

    始终只使用同一个产品族中的对象。

  3. 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点:

  1. 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需

    要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。

3.5 适用场景

(1) 系统中有多于一个的产品族。而每次只使用其中某一产品族。可以通过

配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产

品族。

(2) 产品等级结构稳定。设计完成之后,不会向系统中增加新的产品等级结

构或者删除已有的产品等级结构。

4 单例模式

定义:保证 一个类只有一个实例存在,同时 提供能对该实例加以访问的全局

访问方法

要点

  1. 某个类只能有一个实例

  2. 它必须自行创建这个实例

  3. 它必须自行向整个系统提供这个实例

4.1 模式中的角色和职责

Singleton(单例):在单例类的内部实现 只生成一个实例,同时它提供一个 静态的 getInstance() 工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其 构造函数设计为私有;在 单例类内部定义了一个 Singleton 类型的静态对象,作为外部共享的唯一实例。

单例模式的使用步骤

  1. 构造函数私有化。

  2. 提供一个全局的静态方法(全局访问点)来获取单例对象。

  3. 在类中定义一个静态指针,指向本类的变量的静态变量指针 。

4.2 案例

单例模式的 两种实现方式

  1. 懒汉式:在调用全局静态方法,获取单例的时候再进行创建。懒汉式的单例创建在程序的执行中进行。

  2. 饿汉式:在声明的时候就创建出来单例。饿汉式的单例是在编译的时候就已经创建好了。

示例代码如下:

#include <iostream>

using namespace std;

// 懒汉模式
class Singelton_lazy
{
public: // 对外提供一个全局的静态方法
static Singelton_lazy* getInstance()
{
// 懒汉式:在调用全局静态方法获取单例的时候,再进行创建。
if(instance == NULL)
{
instance = new Singelton_lazy;
} m_count++; return instance;
} int getCount()
{
return m_count;
} private: // 构造函数私有化
Singelton_lazy()
{
instance = NULL;
m_count = 0; cout<< "构造函数 Singelton_lazy() 执行" << endl;
} // 在类中定义一个静态指针,指向本类的变量的静态变量指针
static Singelton_lazy* instance;
static int m_count;
}; // 对静态变量的初始化要放在全局位置上
Singelton_lazy* Singelton_lazy::instance = NULL;
int Singelton_lazy::m_count = 0; // 饿汉模式
class Singelton_hungry
{
public: static Singelton_hungry* getInstance()
{
m_count++; return instance;
} int getCount()
{
return m_count;
} private: Singelton_hungry()
{
instance = NULL;
m_count = 0; cout<< "构造函数 Singelton_hungry() 执行" << endl;
} static Singelton_hungry* instance;
static int m_count;
}; // 饿汉式的单例是在声明的时候就创建出来,所以在编译的时候就已经创建好了。
Singelton_hungry* Singelton_hungry::instance = new Singelton_hungry;
int Singelton_hungry::m_count = 0; int main(void)
{
cout << "--- 以下是懒汉式 ---" << endl; Singelton_lazy* singelton_lazy1 = Singelton_lazy::getInstance();
cout << singelton_lazy1->getCount() << endl; Singelton_lazy* singelton_lazy2 = Singelton_lazy::getInstance();
cout << singelton_lazy2->getCount() << endl; if(singelton_lazy1 == singelton_lazy2)
{
cout << "二者是同一个实例" << endl;
}
else
{
cout << "二者不是同一个实例" << endl;
} cout << "--- 以下是饿汉式 ---" << endl; Singelton_hungry* singelton_hungry1 = Singelton_hungry::getInstance();
cout << singelton_hungry1->getCount() << endl; Singelton_hungry* singelton_hungry2 = Singelton_hungry::getInstance();
cout << singelton_hungry2->getCount() << endl; if(singelton_hungry1 == singelton_hungry2)
{
cout << "二者是同一个实例" << endl;
}
else
{
cout << "二者不是同一个实例" << endl;
}
}

运行结果:

4.3 优缺点

优点

(1) 单例模式提供了对唯一实例的受控访问。

(2) 节约系统资源。由于在系统内存中只存在一个对象。

缺点

(1) 扩展略难。单例模式中没有抽象层。

(2) 单例类的职责过重

4.4 适用场景

(1) 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器

或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。

(2) 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问

点,不能通过其他途径访问该实例。

C++ 设计模式 2:创建型模式的更多相关文章

  1. Java设计模式之创建型模式

    创建型模式分为五类:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式 一.工厂方法模式:接口-实现类.工厂类

  2. GoF的23种设计模式之创建型模式的特点和分类

    创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”.这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成.就像我们去商场购买商品时, ...

  3. Typescript玩转设计模式 之 创建型模式

    作者简介 joey 蚂蚁金服·数据体验技术团队 前言 我们团队的工作是用单页面应用的方式实现web工具.涉及到数万到十数万行的前端代码的管理,而且项目周期长达数年. 怎么样很好地管理好这种量级的前端代 ...

  4. Python与设计模式之创建型模式及实战

    用Python学习一下设计模式,如果很枯燥的话,就强行能使用的就用一下.设计模式参考Python与设计模式-途索 1. 单例模式 保证一个类仅有一个实例,并提供一个访问它的全局访问点. import ...

  5. 设计模式01 创建型模式 - 单例模式(Singleton Pattern)

    参考 [1] 设计模式之:创建型设计模式(6种) | 博客园 [2] 单例模式的八种写法比较 | 博客园 单例模式(Singleton  Pattern) 确保一个类有且仅有一个实例,并且为客户提供一 ...

  6. [C#]设计模式-单例模式-创建型模式

    单例模式用于在整个软件系统当中保持唯一实例,在 C# 当中最能够体现此概念的就是静态类,静态类的生命周期是跟随整个程序,并且在整个程序中仅保有一个实例. 不过在这里我们不再详细阐述单例模式与静态类有什 ...

  7. (转自精通Python设计模式)Python设计模式之创建型模式——2.建造者模式

    建造者模式将一个复杂对象的构造过程与其表现分离,这样,同一个构造过程可用于创建多个不同的表现. 我们来看个实际的例子,假设我们想要创建一个HMTL页面生成器,HTML页面的基本结构(构造组件)通常是一 ...

  8. 设计模式01 创建型模式 - 原型模式(Protype Pattern)

    参考 1. 设计模式:原型模式 | 博客园 2. Java clone深拷贝.浅拷贝 | CSDN 3. Cloneable接口和Object的clone()方法 | 博客园 原型模式(Prototy ...

  9. Python版设计模式: 创建型模式:单例模式和工厂模式家族

    一. 单例模式(Singleton) 所谓单例模式,也就是说不管什么时候都要确保只有一个对象实例存在.很多情况下,整个系统中只需要存在一个对象,所有的信息都从这个对象获取,比如系统的配置对象,或者是线 ...

  10. python设计模式1:创建型模式

    1.原型模式 如果想根据现有的对象复制出新的对象并进行修改,可以考虑“原型模式”,而无需知道任何创建细节.(有点像写轮眼...你不需要知道它) import copy class Point: __s ...

随机推荐

  1. 如何快速构建React组件库

    前言 俗话说:"麻雀虽小,五脏俱全",搭建一个组件库,知之非难,行之不易,涉及到的技术方方面面,犹如海面风平浪静,实则暗礁险滩,处处惊险- 目前团队内已经有较为成熟的 Vue 技术 ...

  2. cocos creator屏幕适配的一些知识点

    一. cocos creator 提供的几种适配策略 EXACT_FIT: 整个应用程序在指定区域可见,无需尝试保留原始纵横比.可能会出现失真,应用程序会被拉伸或压缩.也就是说设计分辨率的长和宽不会等 ...

  3. day04 Pyhton学习

    一.上节课内容回顾 字符串 由','','''',""'"括起来的内容是字符串 字符:单一文字符号 字符串:把字符连成串(有顺序的) 索引和切片 s[start: end ...

  4. cmake引入三方库

    目录结构 . |-- cmake | |-- CompilerSettings.cmake | |-- Options.cmake | `-- ProjectJsonCpp.cmake |-- CMa ...

  5. main函数标准写法

    main函数签名 C++中允许两种格式,带参数的和不带参数的: int main() int main(int argc, const char* argv[]) 和C标准不同,C++中main函数必 ...

  6. thinkpad怎么设置u盘启动

    1.按下笔记本的开机键,当屏幕出现"Press F1 for ThinkPad BIOS Setup Utility"提示时,迅速按下f1键,此时系统就自动进入到ThinkPad的 ...

  7. MySQL历史

    MySQL历史 马云生气了 去IOE活动 1979年 研发一个引擎 1996年 发布MySQL1.0 1999年 瑞典注册AB公司 2003年 MySQL 5.0版本 提供试图.存储过程 具有了一些企 ...

  8. vue中跳转页面逻辑

    跳转详情页面具体代码 写这个页面需要安装两个 1.安装axios命令 Cnpm install axios --save 2.安装vant Cnpm install vant --save 在inde ...

  9. 彻底理解RSA加密算法

    RSA是非常典型的非对称加密算法 它的算法是这样的 加密是我们把明文M转化成密文C 需要用到加密运算 而解密时我们要用解密运算将密文C转化成M 从表达式中 可以看出 e和d使我们需要确定的参数 而N是 ...

  10. IDEA操作git的一些常用技巧

    转自:https://blog.csdn.net/ck4438707/article/details/53455962 Git原理以后会分章节介绍,本次主要说一下intellij怎样操作git.int ...