• 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类是一个“巨大的”类,在该类的设计中存在如下几个问题:

    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) 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问
    点,不能通过其他途径访问该实例。

  • 相关阅读:
    Linq的一些常见Demo
    有一名员工发现日历已经7天没有翻了,于是他连着翻了7页,7天的总和刚好是138,问这一天是几号?
    20块钱,1块钱1瓶,两个空瓶子可以换一瓶,问最多可以喝几瓶?
    【转】Java编程之字符集问题研究
    Reset / Validate Buffer
    Article Master Data Deviation
    STAD Parameters
    Linux11.2 MySQL常用命令
    Linux11.1 设置更改Mysql的root密码及连接mysql
    Linux5.10 告警系统
  • 原文地址:https://www.cnblogs.com/PikapBai/p/13406263.html
Copyright © 2020-2023  润新知