C++ 设计模式 2:创建型模式
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类是一个“巨大的”类,在该类的设计中存在如下几个问题:
在Fruit类中包含很多“if…else…”代码块,整个类的代码相当冗长,
代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在
还将影响系统的性能,程序在执行过程中需要做大量的条件判断。Fruit类的职责过重,它负责初始化和显示所有的水果对象,将各种水
果对象的初始化代码和显示代码集中在一个类中实现,违反了“单一职责原
则”,不利于类的重用和维护。当需要增加新类型的水果时,必须修改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.4 适用场景
工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂
方法中的业务逻辑太过复杂。客户端只知道传入工厂类的参数,对于如何创建对象并不关心
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 优缺点
优点:
不需要记住具体类名,甚至连具体参数都不用记忆。
实现了对象创建和使用的分离。
系统的可扩展性也就变得非常好,无需修改接口和原类。
缺点:
增加系统中类的个数,复杂度和理解度增加。
增加了系统的抽象性和理解难度。
2.4 适用场景
客户端不知道它所需要的对象的类。
抽象工厂类通过其子类来指定创建哪个对象。
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 优缺点
优点:
拥有工厂方法模式的优点
当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端
始终只使用同一个产品族中的对象。增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点:
- 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需
要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。
3.5 适用场景
(1) 系统中有多于一个的产品族。而每次只使用其中某一产品族。可以通过
配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产
品族。
(2) 产品等级结构稳定。设计完成之后,不会向系统中增加新的产品等级结
构或者删除已有的产品等级结构。
4 单例模式
定义:保证 一个类只有一个实例存在,同时 提供能对该实例加以访问的全局
访问方法。
要点:
某个类只能有一个实例
它必须自行创建这个实例
它必须自行向整个系统提供这个实例
4.1 模式中的角色和职责
Singleton(单例):在单例类的内部实现 只生成一个实例,同时它提供一个 静态的 getInstance()
工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其 构造函数设计为私有;在 单例类内部定义了一个 Singleton 类型的静态对象,作为外部共享的唯一实例。
单例模式的使用步骤:
构造函数私有化。
提供一个全局的静态方法(全局访问点)来获取单例对象。
在类中定义一个静态指针,指向本类的变量的静态变量指针 。
4.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:创建型模式的更多相关文章
- Java设计模式之创建型模式
创建型模式分为五类:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式 一.工厂方法模式:接口-实现类.工厂类
- GoF的23种设计模式之创建型模式的特点和分类
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”.这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成.就像我们去商场购买商品时, ...
- Typescript玩转设计模式 之 创建型模式
作者简介 joey 蚂蚁金服·数据体验技术团队 前言 我们团队的工作是用单页面应用的方式实现web工具.涉及到数万到十数万行的前端代码的管理,而且项目周期长达数年. 怎么样很好地管理好这种量级的前端代 ...
- Python与设计模式之创建型模式及实战
用Python学习一下设计模式,如果很枯燥的话,就强行能使用的就用一下.设计模式参考Python与设计模式-途索 1. 单例模式 保证一个类仅有一个实例,并提供一个访问它的全局访问点. import ...
- 设计模式01 创建型模式 - 单例模式(Singleton Pattern)
参考 [1] 设计模式之:创建型设计模式(6种) | 博客园 [2] 单例模式的八种写法比较 | 博客园 单例模式(Singleton Pattern) 确保一个类有且仅有一个实例,并且为客户提供一 ...
- [C#]设计模式-单例模式-创建型模式
单例模式用于在整个软件系统当中保持唯一实例,在 C# 当中最能够体现此概念的就是静态类,静态类的生命周期是跟随整个程序,并且在整个程序中仅保有一个实例. 不过在这里我们不再详细阐述单例模式与静态类有什 ...
- (转自精通Python设计模式)Python设计模式之创建型模式——2.建造者模式
建造者模式将一个复杂对象的构造过程与其表现分离,这样,同一个构造过程可用于创建多个不同的表现. 我们来看个实际的例子,假设我们想要创建一个HMTL页面生成器,HTML页面的基本结构(构造组件)通常是一 ...
- 设计模式01 创建型模式 - 原型模式(Protype Pattern)
参考 1. 设计模式:原型模式 | 博客园 2. Java clone深拷贝.浅拷贝 | CSDN 3. Cloneable接口和Object的clone()方法 | 博客园 原型模式(Prototy ...
- Python版设计模式: 创建型模式:单例模式和工厂模式家族
一. 单例模式(Singleton) 所谓单例模式,也就是说不管什么时候都要确保只有一个对象实例存在.很多情况下,整个系统中只需要存在一个对象,所有的信息都从这个对象获取,比如系统的配置对象,或者是线 ...
- python设计模式1:创建型模式
1.原型模式 如果想根据现有的对象复制出新的对象并进行修改,可以考虑“原型模式”,而无需知道任何创建细节.(有点像写轮眼...你不需要知道它) import copy class Point: __s ...
随机推荐
- 二进制部署Redis-5.07
Redis简介 Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理. 它支持字符串.哈希表.列表.集合.有序集合,位图,hyperloglogs等数据类 ...
- Linux中断驱动程序
1.中断概念 中断时一种电信号,由硬件设备产生,然后再由中断控制器向处理器发送相应的信号.处理器一经检测到该信号,便中断自己当前正在处理的工作,转而去处理中断.此后,处理器会通知操作系统已经产生中断. ...
- leaflet如何加载10万数据
作为一名GIS开发者,你工作中一定遇到过这种问题,根据业务设计,需要在地图上添加1万+条数据,数据或是点.或是线.或是面.但不管哪种,当你添加到5000条时,地图操作就会出现明显的卡顿.当你添加超过1 ...
- es6深层次数组深拷贝
let arr = [ { label: '1', children: [1, 2] } ] let a = [{...arr[0]}] ...
- 以太坊blockchain源码分析
blockchain关键元素 db:持久化到底层数据储存,即leveldb: genesisBlock:创始区块 currentBlock:当前区块,blockchain中并不是储存链所有的block ...
- 方格取数(简单版)+小烈送菜(不知道哪来的题)-----------奇怪的dp增加了!
一.方格取数: 设有N*N的方格图(N<=20),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0. 某人从图的左上角的A(1,1) 点出发,可以向下行走,也可以向右走,直到到达右下 ...
- 如何使用 Gin 和 Gorm 搭建一个简单的 API 服务 (三)
修改数据结构 基本的 API 已经定义好了,现在是个修改 Person 对象结构的好时机.只要修改 Person 结构体,数据库和 API 都会自动做出相应的修改. 我要做的是在 Person ...
- 第十八章 HTTPS介绍及实战演练
一.HTTPS介绍 1.概述 为什么需要使用HTTPS,因为HTTP不安全,当我们使用http网站时,会遭到劫持和篡改,如果采用https协议,那么数据在传输过程中是加密的,所以黑客无法窃取或者篡改数 ...
- 第二章 rsync服务原理
一.备份 1.什么是备份? 1)把重要的数据或者文件再次复制一份并保存下来 2.为什么要做备份? 1)数据的重要性 2)为了出现故障,恢复数据 3.能不能不备份? 1)重要的数据一定要备份 2)不重要 ...
- 一个鲜为人知但很实用的Windows使用技巧
作为一个电脑党,最无法忍受的就是每次开机都要手动打开那些必要的程序.有没办法让这些程序自动打开呢?今天小编意外地得到了一个方法,现在分享给大家. (以腾讯桌面整理为例) 1,Win + R 2,输入t ...