【目录】

设计模式学习之路1----设计模式的三大分类及六大原则
设计模式学习之路2----创建型模式详解
设计模式学习之路3----结构型模式详解上
设计模式学习之路4----结构型模式详解下
设计模式学习之路5----行为型模式详解上
设计模式学习之路6----行为型模式详解下

本博客专门介绍创建型模式

 

1、简单工厂模式

工厂模式提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

实例

我们先创建一个表示动物通用特性的接口

class Animal{

public:
    //虚函数
    virtual void run();
};

再随便创建两个实现类

class Cat:public Animal{
public:
    void run()
    {
        printf("Cat\n");
    }
};
class Dog:public Animal{
public:
    void run()
    {
        printf("Dog\n");
    }
};

最后我们测试一下工厂类的作用

【代码】

//组合
#include<bits/stdc++.h>
using namespace std;

//接口
class Animal{

public:
    //纯虚函数,不用纯虚函数编译会报错
    virtual void run() = 0;
};

class Cat:public Animal{
public:
    void run()
    {
        printf("Cat\n");
    }
};
class Dog:public Animal{
public:
    void run()
    {
        printf("Dog\n");
    }
};

class AnimalFactory{
public:
    static Animal *create(string name)
    {

        if(name=="Cat"){
            return new Cat();
        }
        if(name=="Dog"){
            return new Dog();
        }
        return NULL;
    }
};

int main(){

    Animal *cat = AnimalFactory::create("Cat");
    cat->run();
    Animal *dog = AnimalFactory::create("Dog");
    dog->run();
    Animal *other = AnimalFactory::create("sdg");
    other->run();
}

【运行结果】

设计模式学习之路2----创建型模式详解_c++

2、工厂方法模式(Factory Method)


简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到工厂方法模式,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

示例

先创建一个动物接口

class Animal{

public:
    //纯虚函数
    virtual void run() = 0;
};

随便创建两个实现类

class Cat:public Animal{
public:
    void run()
    {
        printf("Cat\n");
    }
};
class Dog:public Animal{
public:
    void run()
    {
        printf("Dog\n");
    }
};

再提供一个工厂接口

class Factory{
public:
    virtual Animal *CreateAnimal() = 0;
};

提供两个工厂类

class CatFactory:public Factory{
public:
    Animal *CreateAnimal(){
        return new Cat();
    }
};
class DogFactory:public Factory{
public:
    Animal *CreateAnimal(){
        return new Dog();
    }
};

【测试代码】

//组合
#include<bits/stdc++.h>
using namespace std;

//接口
class Animal{

public:
    //纯虚函数
    virtual void run() = 0;
};

class Cat:public Animal{
public:
    void run()
    {
        printf("Cat\n");
    }
};
class Dog:public Animal{
public:
    void run()
    {
        printf("Dog\n");
    }
};

class Factory{
public:
    virtual Animal *CreateAnimal() = 0;
};

class CatFactory:public Factory{
public:
    Animal *CreateAnimal(){
        return new Cat();
    }
};
class DogFactory:public Factory{
public:
    Animal *CreateAnimal(){
        return new Dog();
    }
};

int main(){

    //创建一个猫厂
    CatFactory *catFactory = new CatFactory();
    //创建一只猫
    Animal *cat = catFactory->CreateAnimal();
    cat->run();
    //创建一个够厂
    DogFactory *dogFactory = new DogFactory();
    //创建一条狗
    Animal *dog = dogFactory->CreateAnimal();
    dog->run();
}

【测试结果】

设计模式学习之路2----创建型模式详解_设计模式_02

 

【总结】

这个模式的最大的好处在于不必修改现有的代码,拓展性较好!但是可能需要的类较多,使程序变得臃肿。

3、抽象工厂模式(Abstract Factory Pattern)

与工厂方法的区别:工厂方法可以说是抽象工厂的一个特例,当工厂里只有一种产品的时候,抽象工厂的结构就简化成工厂方法的结构了。


在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

我们换一种容易理解的方式来讲解抽象工厂模式,例如我们需要键盘和鼠标来组装电脑,华为电脑需要配置华为键盘和华为鼠标,小米电脑需要配置小米键盘和小米鼠标。那么使用抽象工厂模式,在为电脑生产相关配件时,就无需制定配件的型号,它会自动根据电脑类型生产对应的配件。

注意事项
产品族难扩展,产品等级易扩展。

例子
QQ 换皮肤,一整套一起换。

示例
创建鼠标类

//鼠标类
class Mouse{
};
//华为鼠标类
class HuaWeiMouse{
public:
    void say()
    {
        printf("这是一个华为鼠标类\n");
    }
};
//小米鼠标类
class XiaoMiMouse{
public:
    void say()
    {
        printf("这是一个小米鼠标类\n");
    }
};

创建键盘类

//键盘类
class KeyBoard{
};
//华为键盘类
class HuaWeiKeyBoard:public KeyBoard{
public:
    void say()
    {
        printf("这是一个华为键盘类\n");
    }
};
//小米鼠标类
class XiaoMiKeyBoard:public KeyBoard{
public:
    void say()
    {
        printf("这是一个小米键盘类\n");
    }
};

创建电脑工厂接口

//创建一个抽象工厂类
class AbstractFactory{
public:
    virtual Mouse *CreateMouse() = 0;
    virtual KeyBoard *CreateKeyBoar() = 0;
};

然后创建华为电脑、小米电脑

//创建华为电脑工厂
class HuaWeiComputer:public AbstractFactory{
public:
    Mouse *CreateMouse(){
        return new HuaWeiMouse();
    }
    KeyBoard *CreateKeyBoar(){
        return new HuaWeiKeyBoard();
    }
};
//创建小米电脑工厂
class XiaoMiComputer:public AbstractFactory{
public:
    Mouse *CreateMouse(){
        return new XiaoMiMouse();
    }
    KeyBoard *CreateKeyBoar(){
        return new XiaoMiKeyBoard();
    }
};

【完整代码测试】

//组合
#include<bits/stdc++.h>
using namespace std;
//鼠标类
class Mouse{

};
//华为鼠标类
class HuaWeiMouse:public Mouse{
public:
    void say()
    {
        printf("这是一个华为鼠标类\n");
    }
};
//小米鼠标类
class XiaoMiMouse:public Mouse{
public:
    void say()
    {
        printf("这是一个小米鼠标类\n");
    }
};


//键盘类
class KeyBoard{
};
//华为键盘类
class HuaWeiKeyBoard:public KeyBoard{
public:
    void say()
    {
        printf("这是一个华为键盘类\n");
    }
};
//小米鼠标类
class XiaoMiKeyBoard:public KeyBoard{
public:
    void say()
    {
        printf("这是一个小米键盘类\n");
    }
};


//创建一个抽象工厂类
class AbstractFactory{
public:
    virtual Mouse *CreateMouse() = 0;
    virtual KeyBoard *CreateKeyBoar() = 0;
};

//创建华为电脑工厂
class HuaWeiComputer:public AbstractFactory{
public:
    Mouse *CreateMouse(){
        return new HuaWeiMouse();
    }
    KeyBoard *CreateKeyBoar(){
        return new HuaWeiKeyBoard();
    }
};
//创建小米电脑工厂
class XiaoMiComputer:public AbstractFactory{
public:
    Mouse *CreateMouse(){
        return new XiaoMiMouse();
    }
    KeyBoard *CreateKeyBoar(){
        return new XiaoMiKeyBoard();
    }
};
int main(){
    //创建一个华为工厂
    HuaWeiComputer* huawei = new HuaWeiComputer();
    //这里不同于java,需要强制转换
    HuaWeiMouse* huaweimouse = (HuaWeiMouse*)huawei->CreateMouse();
    huaweimouse->say();

}

【测试结果】

设计模式学习之路2----创建型模式详解_#include_03

与工厂方法的区别:工厂方法可以说是抽象工厂的一个特例,当工厂里只有一种产品的时候,抽象工厂的结构就简化成工厂方法的结构了。

 

比如这个例子,如果只有华为,那么就是工厂方法了

4、单例模式

 

顾名思义,单例模式确保只有单个对象被创建

下面介绍实现单例模式的几种方式

线程不安全的懒汉式

这种方式是最基本的实现方式,且lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

【懒汉式】

这种方式是最基本的实现方式,不要求线程安全,在多线程不能正常工作。懒汉式的特点是延迟加载,比如配置文件,采用懒汉式的方法,配置文件的实例直到用到的时候才会加载

//懒汉式
#include<bits/stdc++.h>
using namespace std;

class Singleton
{
private:
    static Singleton *instance;
    Singleton(){}

public:
    static Singleton* getInstance(){
        if(instance == NULL){
            instance = new Singleton();
        }
        return instance;
    }
};
//不加这句编译不通过
Singleton*Singleton::instance = NULL;

int main(){
    Singleton *it = Singleton::getInstance();

}

【线程安全的懒汉式】

用了互斥量mutex

//线程懒汉式
#include<bits/stdc++.h>
using namespace std;
mutex it;

class Singleton
{
private:
    static Singleton *instance;
    Singleton(){}

public:
    static Singleton* getInstance(){
        it.lock();
        if(instance == NULL){
            instance = new Singleton();
        }
        it.unlock();
        return instance;
    }
};
//不加这句编译不通过
Singleton*Singleton::instance = NULL;

int main(){
    Singleton *it = Singleton::getInstance();

}

【饿汉式】

没有 Lazy 初始化,容易产生垃圾对象。多线程安全,没有加锁,执行效率会提高。

//饿汉式
#include<bits/stdc++.h>
using namespace std;
mutex it;

class Singleton
{
private:
    static Singleton *instance;
    Singleton(){}

public:
    static Singleton* getInstance(){
        return instance;
    }
    void print()
    {
        cout << "this = " << this << endl;
    }

};

//与懒汉式差别的地方
Singleton* Singleton::instance = new Singleton();

int main(){
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();

    s1->print();
    s2->print();

}

双检锁/双重校验锁(DCL,即 double-checked locking)

登记式/静态内部类

这两类就没有深入去了解了

 

5、建造者模式


无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮、方向盘、发送机等各种部件。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车,可以通过建造者模式对其进行设计与描述,建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。

建造者模式(Builder Pattern)可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。它一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

优点
    (1 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
    (2 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,
    (3 用户使用不同的具体建造者即可得到不同的产品对象 。
    (4 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
    ( 5 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合 “开闭原则”
缺点
    (1 产品之间差异性很大的情况:建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
    (2 产品内部变化很复杂的情况: 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
抽象工厂模式与建造者模式的区别
    抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。

示例
我们先建造一个具体的产品对象 – 单车

//自行车的产品对象
class BikeProduct
{
private:
    //轮胎
    string tyre;
    //座椅
    string seat;
public:
    BikeProduct(){}
    string getTyre(){
        return tyre;
    }
    string getSeat(){
        return seat;
    }
    void setTyre(string a){
        tyre = a;
    }
    void setSeat(string a){
        seat = a;
    }
};

接着,我们构建一个抽象建造者Builder,用于创建产品对象的各个指定部件

//自行车建造者抽象类
class BikeBuilder{

public:
    BikeProduct *bike = new BikeProduct();
    virtual void buildTyre()=0;
    virtual void buildSeat()=0;
    BikeProduct* getBike(){
        return bike;
    }
};

然后我们构建具体建造者,用于构建和装配各个部件

//摩拜单车  具体构建者
class Mobiebuilder:public BikeBuilder
{
public:
    void buildTyre(){
        bike->setTyre("摩拜轮胎");
    }
    void buildSeat(){
        bike->setSeat("摩拜座位");
    }
};

//ofo单车  具体构建者
class Ofobuilder:public BikeBuilder
{
public:
    void buildTyre(){
        bike->setTyre("ofo轮胎");
    }
    void buildSeat(){
        bike->setSeat("ofo座位");
    }
};

接着我们构建一个指挥者,使用Builder接口类装配对象。它不仅可以隔离了客户与对象的生产过程,也负责控制产品对象的生产过程。

//指挥者
class Director{
private:
    BikeBuilder* bikeBuilder;
public:
    void setBikeBuilder(BikeBuilder *a){
        bikeBuilder = a;
    }
    //组装
    BikeProduct* construct(){
        bikeBuilder->buildTyre();
        bikeBuilder->buildSeat();
        return bikeBuilder->bike;
    }
};

【完整测试代码】

//建造者模式
#include<bits/stdc++.h>
using namespace std;

//自行车的产品对象
class BikeProduct
{
private:
    //轮胎
    string tyre;
    //座椅
    string seat;
public:
    BikeProduct(){}
    string getTyre(){
        return tyre;
    }
    string getSeat(){
        return seat;
    }
    void setTyre(string a){
        tyre = a;
    }
    void setSeat(string a){
        seat = a;
    }
};

//自行车建造者抽象类
class BikeBuilder{

public:
    BikeProduct *bike = new BikeProduct();
    virtual void buildTyre()=0;
    virtual void buildSeat()=0;
    BikeProduct* getBike(){
        return bike;
    }
};
//BikeBuilder* BikeBuilder::bike = new BikeBuilder();
//摩拜单车  具体构建者
class Mobiebuilder:public BikeBuilder
{
public:
    void buildTyre(){
        bike->setTyre("摩拜轮胎");
    }
    void buildSeat(){
        bike->setSeat("摩拜座位");
    }
};

//ofo单车  具体构建者
class Ofobuilder:public BikeBuilder
{
public:
    void buildTyre(){
        bike->setTyre("ofo轮胎");
    }
    void buildSeat(){
        bike->setSeat("ofo座位");
    }
};

//指挥者
class Director{
private:
    BikeBuilder* bikeBuilder;
public:
    void setBikeBuilder(BikeBuilder *a){
        bikeBuilder = a;
    }
    //组装
    BikeProduct* construct(){
        bikeBuilder->buildTyre();
        bikeBuilder->buildSeat();
        return bikeBuilder->bike;
    }
};

int main(){
    Director *it = new Director();
    //建造摩拜单车
    it->setBikeBuilder(new Mobiebuilder());
    BikeProduct *mobie = it->construct();
    cout<<mobie->getTyre()<<endl;
    cout<<mobie->getSeat()<<endl;
   //建造ofo单车
    it->setBikeBuilder(new Ofobuilder());
    mobie = it->construct();
    cout<<mobie->getTyre()<<endl;
    cout<<mobie->getSeat()<<endl;
}

【测试结果】

设计模式学习之路2----创建型模式详解_#include_04

6、原型模式


若通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。原型模式以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点。

克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,再修改克隆对象的值。

优势
效率高(直接克隆,避免了重新执行构造步骤)。

浅克隆与深克隆的区别
浅克隆:复制该对象,然后保留该对象原有的引用。也就是说不克隆该对象的属性。

深克隆:复制该对象,并且把该对象的所有属性也克隆出一份新的。

深克隆的实现

创建了一个类模板

template<class T>
class ICloneable
{
public:
	virtual T* clone() = 0;
};

接着创建一个简历类,继承ICloneable,并实现其纯虚函数

// 简历类
class CResume : public ICloneable<CResume>
{
protected:
	string m_name;
	string m_sex;
	int m_age;

public:
	CResume(){}
	void setInfo(const string& name, const string& sex, int age)
	{
		m_name = name;
		m_sex = sex;
		m_age = age;
	}



	CResume* clone()
	{
		CResume* resume = new CResume;
		resume->setInfo(m_name, m_sex, m_age);
		return resume;
	}

	void printInfo()
	{
		cout << "Name: " << m_name << endl;
		cout << "Sex: " << m_sex << endl;
		cout << "Age: " << m_age << endl;
		cout << endl;
	}

};

【完整测试代码】

#include <iostream>
#include <string>
#include <memory>

using namespace std;

template<class T>
class ICloneable
{
public:
	virtual T* clone() = 0;
};



// 简历类
class CResume : public ICloneable<CResume>
{
protected:
	string m_name;
	string m_sex;
	int m_age;

public:
	CResume(){}
	void setInfo(const string& name, const string& sex, int age)
	{
		m_name = name;
		m_sex = sex;
		m_age = age;
	}



	CResume* clone()
	{
		CResume* resume = new CResume;
		resume->setInfo(m_name, m_sex, m_age);
		return resume;
	}

	void printInfo()
	{
		cout << "Name: " << m_name << endl;
		cout << "Sex: " << m_sex << endl;
		cout << "Age: " << m_age << endl;
		cout << endl;
	}

};


int main(void)
{
	CResume re;
	re.setInfo("Jacky", "Male", 20);
	re.printInfo();

	CResume* pClone = re.clone();
	pClone->setInfo("Marry", "Female", 30);
	pClone->printInfo();

    re.printInfo();

	delete pClone;
	pClone = NULL;
	return 0;
}

【测试结果】

可以看到,当pClone改变值后,re的信息没有被改变,就是深拷贝

设计模式学习之路2----创建型模式详解_设计模式_05

浅拷贝实现

仅仅在深拷贝的clone函数改变了写法,没有调用构造函数了,改成直接将this指针赋值

#include <iostream>
#include <string>
#include <memory>

using namespace std;

// 克隆接口
template<class T>
class ICloneable
{
public:
	virtual T* clone() = 0;
};



// 简历类
class CResume : public ICloneable<CResume>
{

protected:
	string m_name;
	string m_sex;
	int m_age;


public:

    CResume(){}
	// 带参拷贝构造函数
	CResume(CResume& right)
	{
		m_name = right.m_name;
		m_sex = right.m_sex;
		m_age = right.m_age;
	}

	void setInfo(const string& name, const string& sex, int age)
	{
		m_name = name;
		m_sex = sex;
		m_age = age;
	}

	CResume* clone()
	{
		// 指针赋值 浅拷贝
		CResume* resume = this;
		return resume;
	}

	void printInfo()
	{
		cout << "Name: " << m_name << endl;
		cout << "Sex: " << m_sex << endl;
		cout << "Age: " << m_age << endl;
		cout << endl;
	}


};


int main(void)
{
	CResume *re = new CResume();	// 只能这样构造 无参构造被屏蔽掉了
	re->setInfo("Jacky", "Male", 20);
	re->printInfo();

	CResume* pClone = re->clone();	// 指针赋值 浅拷贝
	//delete pWorkExperience;		// 这里如果调用delete 程序会崩溃(因为是共享)
	pClone->setInfo("Marry", "Female", 30);
	pClone->printInfo();
	re->printInfo();	// 我们发现原对象的workExperience属性已经被修改

	delete pClone;
	pClone = NULL;

	return 0;
}

【测试结果】

更改了pClone的信息,会发现,re的信息同样被改变了

设计模式学习之路2----创建型模式详解_建造者模式_06