常用设计模式C++示例1:创建型模式

PS:关于设计模式,推荐参考电子书:设计模式。本博客代码均参考以上电子书,仅供查阅,不便于学习。

 

1. 单例模式

1.1 概念及实现方法

单例模式保证一个类只存在一个实例,同时提供访问该实例的全局节点,该全局节点仅在首次访问的时候初始化。为了实现单例模式,需要做以下操作:

(1) 构造函数均声明为private,这样外部不能进行单例类初始化;

(2) 提供全局访问节点,private的静态类指针,这样所有类只有一个指针;

(3) 提供静态成员方法getInstance(),该方法在首次访问全局节点时初始化,以后再访问时直接返回已初始化的全局访问节点。

 

1.2 C++实现

非线程安全实现:

class Singleton {
private:
    Singleton(string s):m_value(s) {}; //private的构造函数
    static Singleton* m_instance;  //静态成员指针
    string m_value;
public:
    Singleton() = delete;  //除了getInstance能获取实例,其他构造函数一律禁止
    Singleton(Singleton& single) = delete;
    Singleton operator=(Singleton& single) = delete;
    static Singleton* getInstance(string s)  //访问全局节点函数,获得该节点的指针
    {
        if (m_instance == NULL)
            m_instance = new Singleton(s);
        return m_instance;
    }

    string value() const
    {
        return m_value;
    }

};

Singleton* Singleton::m_instance = nullptr; //静态成员变量只能在类外初始化

因为该类在getInstance()成员函数中没有对m_instance(相当于全局变量)加锁,所以该类是非线程安全的。通过下面代码来验证:

 1 void ThreadBar()
 2 {
 3     //std::this_thread::sleep_for(std::chrono::milliseconds(1000));
 4     Singleton* instance = Singleton::getInstance("Bar");
 5     cout << instance->value() << "\n";
 6 }
 7 
 8 void ThreadFoo()
 9 {
10     //std::this_thread::sleep_for(std::chrono::milliseconds(1000));
11     Singleton* instance = Singleton::getInstance("Foo");
12     cout << instance->value() << "\n";
13 }
14 
15 
16 int main()
17 {
18     thread th1(ThreadBar);
19     thread th2(ThreadFoo);
20     th1.join();
21     th2.join();
22 
23     return 0;
24 }
View Code

上面的代码通过两个线程去初始化实例,运行时,会出现“Foo"和”Bar"同时打印,说明进行了两次实例化。为了修改为线程安全,在getInstance函数中需要添加锁。修改后的getInstance()如下:

class Singleton {
private:
    static mutex m_mutex; //添加锁
public:
    static Singleton* getInstance(string s)
    {
        if (m_instance == NULL)
        {
            lock_guard<mutex> lcok(m_mutex);  //上锁
            if(m_instance==NULL)
                m_instance = new Singleton(s);
        }
        return m_instance;
    }
}

 

PS:关于简单工厂模式,工厂模式,抽象工厂模式的区别,可以参考简单工厂模式,工厂模式,抽象工厂模式,也可以参考本博客第四节。

2. 工厂模式

2.1 工厂模式概念及实现要点

  工厂模式就是设计一个公共的接口和对应的操作函数。将具体的各个类用抽基类指针或引用表达,具体的操作函数用基类的函数去表达,相当于基类成为具体类的一个接口。所有对具体类的操作都通过基类实现。一个工厂模式有四大要素:

(1) product(所有产品类的抽象类,包含对应的操作函数接口),一般为纯虚类;

(2 )concreteProduct(继承product,实现具体的操作函数),为实际产品类;

(3) creator,声明创建函数,能创建不同的concreteProduct,同时声明操作接口,能调用concreteProduct对应的操作接口,一般为纯虚类;

(4) concreteCreator,创建一个具体concreteProduct的类。

可以看到,以上四要素有两个纯虚基类,用于声明接口。两个字类,concreteCreator用于创建具体的concreteProduct。

对应的一个关系如下图所示:

 

 

 2.2  C++实现

首先需要实现四个类:

 1 //产品类,声明不同的产品接口,纯虚类
 2 class product {
 3 public:
 4     virtual ~product() {};
 5     virtual string operation() const = 0;  //操作接口
 6 };
 7 
 8 //具体产品类,实现相同的操作接口
 9 class detailedProduct1:public product
10 {
11 public:
12     //实现具体的产品接口
13     string operation()const override {
14         return "operation of detailedProduct 1";
15     }
16 };
17 
18 class detailedProduct2 :public product
19 {
20 public:
21     //实现具体的产品接口
22     string operation()const override {
23         return "operation of detailedProduct 2";
24     }
25 };
26 
27 //生产者类,主要声明两种接口:
28 //1. 声明创建函数,根据参数类型,创建不同的product
29 //2. 声明通用的操作接口,根据创建的product调用对应的操作接口
30 class creator {
31 public:
32     virtual ~creator() {};
33     virtual product* factoryMethod() const = 0;
34     void operation() {
35         product* pro = factoryMethod();
36         cout << pro->operation() << endl;
37         delete pro;
38     }
39 };
40 
41 //具体的创建者类,创建具体的product
42 class product1Creator:public creator
43 {
44 public:
45     product* factoryMethod() const override {
46         return new detailedProduct1();
47     }
48 };
49 
50 class product2Creator :public creator
51 {
52 public:
53     product* factoryMethod() const override {
54         return new detailedProduct2();
55     }
56 };

  在使用时,利用基类creator指针指向具体的子类creator创建的对象。然后就能调用子类product的方法接口,整个过程只有一个基类creator指针被创建。可以看到,工厂方法的好处在于:

  • 当新的子类出现,只需要实现一个继承自product基类的子类product;再实现一个继承自creator基类的子类creator,对应具体的子类prodcut创建,就能调用相关product的操作函数。整个过程不需要修改原代码,只需要添加新的代码即可。

下面看如何调用工厂模式。

int main()
{
    creator* a1 = new product1Creator();
    a1->operation();

    creator* a2 = new product2Creator();
    a2->operation();

    delete a1, a2;
    return 0;
}

 

3. 抽象工厂模式

3.1 概念及实现要点

抽象工厂模式的特点有:

(1) 存在多个抽象产品,纯虚类,用于声明产品的公有属性和接口;

(2) 每个抽象产品有多个具象产品,实现具体的产品接口;

(3) 一个抽象工厂,生产抽象产品,纯虚类,声明生产产品的接口;

(4) 多个具象工厂,一个具象工厂生产每一种抽象产品的一个具象产品,因此一个抽象产品有多少具象产品,通常就有多少个具象工厂。

对应的关系如下图:

 

抽象工厂具有如下特点:

(1) 同一工厂生产的产品是不同种类,性质相似的;

(2) 避免了客户端和具体代码耦合;

(3) 单一职责原则,产品生成代码都在具象工厂内,方便修改;

(4) 开闭原则,在引入新的产品时,应用程序/客户端无需修改程序。

 

3.2 C++代码实现

 3.1中已经说明,实现抽象工厂需要四种类:抽象产品类,具象产品类,抽象工厂类,具象工厂类。

 1 /***********************************
 2 定义抽象产品类A
 3 1. 存在多个抽象产品,比如抽象产品:椅子,桌子,床
 4 2. 一个抽象产品可以对应多个具象产品,比如抽象产品椅子,可以对应:有靠背的椅子,躺椅,无靠背椅子
 5    但都具有相同的接口
 6 ***********************************/
 7 class abstractProductA
 8 {
 9 public:
10     virtual ~abstractProductA() {};
11     virtual string functionA() const = 0;
12 };
13 
14 //具象产品A1,实现抽象产品A的初始化的具象函数接口
15 class concreteProductA1:public abstractProductA
16 {
17 public:
18     string functionA() const override
19     {
20         return "Concrete product A1";
21     }
22 };
23 
24 //具象产品A2,实现抽象产品A的初始化的具象函数接口
25 class concreteProductA2 :public abstractProductA
26 {
27 public:
28     string functionA() const override
29     {
30         return "Concrete product A2";
31     }
32 };
33 
34 //抽象产品B
35 class abstractProductB {
36 public:
37     virtual ~abstractProductB() {};
38     virtual string functionB() const = 0;
39 };
40 
41 //具象产品B1
42 class concreteProductB1 :public abstractProductB
43 {
44 public:
45     string functionB() const override {
46         return "Concrete product B1";
47     }
48 };
49 
50 //具象产品B2
51 class concreteProductB2 :public abstractProductB
52 {
53 public:
54     string functionB() const override {
55         return "Concrete product B2";
56     }
57 };

 然后是工厂类,工厂类的抽象工厂负责定义接口并与客户端代码交互,具象工厂负责具体产品生产:

 1 /****************************************
 2 以上声明了产品,还需要对应的工厂去生成产品,工厂也分两类,抽象工厂和具象工厂
 3 抽象工厂负责声明统一的创建接口,创建不同的抽象产品,比如此处声明了A,B两类抽象产品
 4 *****************************************/
 5 class abstractFactory
 6 {
 7 public:
 8     virtual abstractProductA* createProductA() const = 0;
 9     virtual abstractProductB* createProductB() const = 0;
10 };
11 
12 /********************************************
13 具象工厂,专门生产具体的产品类,通常每个抽象产品类的一个具象产品类组成一组,由一个具象工厂生产
14 比如:此处有抽象产品A,B ,对应具象产品:A1,A2;B1,B2
15 那么有多少种具象产品类,就有多少种具象工厂,每一个具象工厂负责生成所有抽象产品的一个具象产品
16 比如:此处A1,B1为1组,由一个具象工厂生产;A2,B2为一组
17 **********************************************/
18 class concreteFactory1 :public abstractFactory
19 {
20 public:
21     abstractProductA* createProductA() const override {
22         return new concreteProductA1();
23     }
24     abstractProductB* createProductB() const override {
25         return new concreteProductB1();
26     }
27 };
28 
29 class concreteFactory2 :public abstractFactory
30 {
31 public:
32     abstractProductA* createProductA() const override {
33         return new concreteProductA2();
34     }
35     abstractProductB* createProductB() const override {
36         return new concreteProductB2();
37     }
38 };

 接下来是客户端代码,客户端代码提供的参数输入就是一个抽象工厂类,由于该类是纯虚类,需要通过引用或指针指向具象工厂才能传入实现多态。

 1 /***********************************
 2 客户端代码,只与抽象工厂打交道,不跟具象工厂耦合
 3 根据传进来的工厂对象的不同,创建不同的产品,实现不同的产品函数
 4 ************************************/
 5 void clientCode(const abstractFactory& factory)
 6 {
 7     abstractProductA* prodcut_a = factory.createProductA();
 8     abstractProductB* product_b = factory.createProductB();
 9     cout << prodcut_a->functionA() << endl;
10     cout << product_b->functionB() << endl;
11     delete prodcut_a;
12     delete product_b;
13 }
14 
15 
16 
17 int main()
18 {
19     concreteFactory1* factory1 = new concreteFactory1(); //创建具象工厂
20     clientCode(*factory1);
21     delete factory1;
22     return 0;
23 
24 }
View Code

 

4. 简单工厂,工厂和抽象工厂的区别

  以加减乘除算术运算为例,那么此处抽象产品就是算术运算类,命名为arithmeticMethod,具象产品就是addMethod,minusMehod,multiMethod,divideMethod,分别为加减乘除类,那么四种具象类分别实现一个方法getResult,用于实现加减乘除。

4.1 简单工厂

  简单工厂一般只有三个类:抽象产品类,具象产品类,工厂类。工厂类中的createProduct方法包含逻辑控制语句,根据不同的swith分支创建不同的具象产品。下面看加减乘除算术运算的一个具体实现方式:

 1 //抽象产品类
 2 class arithmeticMethod
 3 {public:
 4     virtual ~arithmeticMethod() {};
 5     virtual double getResult(double a1, double a2) const = 0;
 6 };
 7 
 8 //具象产品类
 9 class addMethod:public arithmeticMethod
10 {
11 public:
12     double getResult(double a1, double a2) const override
13     {
14         return a1 + a2;
15     }
16 };
17 
18 //具象产品类
19 class minusMethod :public arithmeticMethod
20 {
21 public:
22     double getResult(double a1, double a2) const override
23     {
24         return a1 - a2;
25     }
26 };
27 
28 //具象产品类
29 class multiMethod :public arithmeticMethod
30 {
31 public:
32     double getResult(double a1, double a2) const override
33     {
34         return a1*a2;
35     }
36 };
37 
38 //具象产品类
39 class divideMethod :public arithmeticMethod
40 {
41 public:
42     double getResult(double a1, double a2) const override
43     {
44         return a1 / a2; //此处不进行类型检测,仅对工厂系列方法进行讲解
45     }
46 };
47 
48 class factory {
49 public:
50     arithmeticMethod* createProduct(char c)
51     {
52         switch (c) {
53         case '+':return new addMethod();    break;
54         case '-':return new minusMethod();  break;
55         case '*':return new multiMethod();  break;
56         case '/':return new divideMethod(); break;
57         }
58     }
59 };
60 
61 //客户端代码
62 void clientCode(factory& f) //抽象产品指针
63 {
64     arithmeticMethod* product = f.createProduct('+');
65     double res=product->getResult(1.2, 2.4);
66     cout << res << endl;
67     delete product;
68 }
69 
70 
71 int main()
72 {
73     
74     factory* f = new factory();
75     clientCode(*f);
76     return 0;
77 
78 }

 简单工厂类所有具象产品的生产均在简单工厂内部,当产品增加时,需要直接修改工厂代码,同时随着产品增加简单工厂的函数内容也会更复杂。

 

4.2 工厂模式

  工厂模式就是把简单工厂进行扩展,分为抽象工厂(只定义生成接口),具象工厂(将简单工厂的switch分支实现为一个具象工厂),这样添加新的类时,只需要添加一个具象工厂类。将4.1的代码修改为工厂模式:

 1 /***********************************
 2 加减乘除的具象产品与4.1同理,只需要将简单工厂扩充为一个纯虚类工厂和多个具象工厂
 3 ************************************/
 4 
 5 class factory {
 6 public:
 7     virtual ~factory() {};
 8     virtual arithmeticMethod* createProduct() const = 0;
 9 };
10 
11 class addFactory:public factory
12 {
13 public:
14     arithmeticMethod* createProduct() const override {
15         return new addMethod();
16     }
17 };
18 
19 class minusFactory :public factory
20 {
21 public:
22     arithmeticMethod* createProduct() const override {
23         return new minusMethod();
24     }
25 };
26 
27 //乘除具象工厂同理,不再赘述
28 
29 
30 //客户端代码
31 void clientCode(factory& f) //抽象产品指针
32 {
33     arithmeticMethod* product = f.createProduct();
34     double res=product->getResult(1.2, 2.4);
35     cout << res << endl;
36     delete product;
37 }
38 
39 
40 int main()
41 {
42     
43     factory* f = new addFactory();
44     clientCode(*f);
45     return 0;
46 
47 }

 

4.3 抽象工厂

  简单工厂和工厂模式均只能生成一种抽象产品,该抽象产品具有多个具象产品。但是当存在多个抽象产品,每个抽象产品又存在多个具象产品时,简单工厂模式和工厂模式就有些捉襟见肘,抽象工厂模式相对于前两种模式的区别就在于:

(1) 抽象工厂模式有多个抽象产品类,每个抽象产品有多个具象产品

(2) 每个具象工厂不再只生产一种产品类,而是生产每个抽象产品的一个具象产品(同一工厂生产的一组具象产品具有同类共同属性)

  4.1,4.2的例子用工厂模式就能很好解决了,再举一个用于抽象工厂的例子(此例子为设计模式-抽象工厂模式中的例子)。假如现在工厂要生产椅子,桌子,沙发三个抽象产品,同时每个抽象产品又有不同风格,比如欧美风,中国风,非洲风。那么相当于有三个抽象产品,每个抽象产品有三个具象产品,因此声明一个抽象工厂,三个具象工厂,每个具象工厂生产一类风格的产品,比如工厂1只生成中国风的椅子,桌子和沙发(即同一具象工厂内生产的产品来自不同抽象产品类,但是相互间又有一致属性)。代码再次不再详述。

 

5. 生成器模式

5.1 概念及实现要点

  在实际编程时,常常遇到某个类有很多属性,在构造函数初始化时,为了类属性初始化,构造函数的参数列表参数非常多。造成调用麻烦。这个时候,可以将构造函数拆分成几个函数,每个函数初始化一部分参数。举个具体的例子,比如创建一个房屋类,该房屋主要由墙,窗户,门,家具,装饰等属性组成,可以用一系列的生成器,每个生成器调用不同的函数,生成不同类型的房屋。如下图所示。

 

   从上面的例子,也可以看出来一个生成器模式需要哪些基本元素:抽象产品类,抽象生成器类(纯虚类),具象生成器类(多个具象生成器)。除此之外,通常还有一个主管(Director),主管传入一个抽象生成器类,调用生成函数,生成最经典的产品(相当于量产产品)。这样客户端代码,就直接跟Director耦合,不必一次调用众多的属性生成函数。各个部件的结构如下图所示。

 

 

5.2 C++实现

按照产品类,抽象生成器类,具象生成器类,主管类,客户端代码五个部分,编写的c++函数如下:

//生成器模式,通过一系列步骤完成初始化,即将构造函数拆分
//产品类
class Product
{
public:
    vector<string> m_parts; //所有属性
    void listParts() const
    {
        for (int i = 0; i < m_parts.size(); i++) {
            cout << m_parts[i] << "  ";
        }
        cout << endl;
    }
};

//抽象生成器,定义公有的特性添加接口
class Builder
{
public:
    virtual ~Builder() {};
    virtual void producePartA() const = 0;
    virtual void producePartB() const = 0;
    virtual void producePartC() const = 0;
};

//具象的生成器1,生成一类特定产品
class ConcreteBuilder1:public Builder
{
private:
    Product* prod;
public:
    ConcreteBuilder1() {
        reset();
    }
    void reset() {
        prod = new Product(); //生成产品基本类型
    }
    ~ConcreteBuilder1() {
        delete prod;
    }
    //生成产品其他具体部件
    void producePartA() const override
    {
        prod->m_parts.push_back("partA1");
    }
    void producePartB() const override
    {
        prod->m_parts.push_back("partB1");
    }

    void producePartC() const override
    {
        prod->m_parts.push_back("partC1");
    }
    //对于某些产品,其除了公有的部件(生产步骤和生产函数),还有其独有的部件,此种生产函数直接放到具体生成器中
    void produceSpecialPart()
    {
        prod->m_parts.push_back("specialPart");
    }

    //注:此处返回已经建造完成的产品,此后builder可以再用于建造其他产品
    //注意删除使用完成的产品
    Product* getProduct()
    {
        Product* p = prod;
        reset();
        return p;
    }
};

//具体生成器2,定义的内容和步骤与生成器1相似

//管理者,主要负责通用的产品生产,就调用concreteBuilder的部件生产函数,实现特定流行产品生产
class Director
{
private:
    Builder* builder;
public:
    void setBuilder(Builder* b)
    {
        builder = b;
    }
    void buildPrevalentProduct()
    {
        builder->producePartB();
        builder->producePartA();
        builder->producePartC();
    }
};

//客户端代码
void clientCode(Director& d)
{
    ConcreteBuilder1* b = new ConcreteBuilder1();
    d.setBuilder(b);
    d.buildPrevalentProduct();
    Product* p = b->getProduct();
    p->listParts();
    delete p;
    delete b;
}

int main()
{
    Director* d = new Director();
    clientCode(*d);
    return 0;
}

需要注意的是ConcreteBuilder(具象生成器类)的getProduct()函数,该函数返回已经调用Director生产完成的产品指针,同时生成一个新的产品指针,因此用完返回的产品需要人为释放空间。

 

6. 原型模式

6.1 概念及实现要点

  当有一个类对象,希望对其进行复制时,常用的方法是遍历原对象类的成员变量,并将其复制到新的对象中(这通常可以用拷贝构造函数完成)。但是这使得客户端代码直接跟具体的类打交道,造成耦合关系。原型模式就是为了解决类复制问题引入的,其主要的结构和对应需要实现的类如下图:

 

 可以看到,实现一个原型模式,主要有抽象原型,具体原型,原型注册表三大类。抽象原型主要定义clone接口,可以复制一个具体原型类;具体原型实现接口;原型注册表将所有可克隆的原型组织成一个HashTable的形式,直接通过原型注册表的具体原型对象,调用其复制接口获得新的具体原型对象。客户端也只跟原型注册表交互。

 

6.2 C++实现

//在此给每个具体原型一个编号,声明原型相关信息,客户端通过这个原型编号创建具体的原型
enum Type {
    PROTOTYPE1 = 0,
    PROTOTYPE2
};

//抽象原型,最主要的是clone接口
class Prototype
{
protected:
    string m_name;
    int m_val;
public:
    Prototype(string name, int val) :m_name(name), m_val(val) {};
    virtual Prototype* clone() const = 0; //克隆函数接口
    virtual void showMems() const = 0;
};

//具象原型1
class ConcretePrototype1:public Prototype
{
private:
    float m_score; //声明其他的一些属性
public:
    ConcretePrototype1(string name, int val, float score) :Prototype(name, val), m_score(score) {};
    //注意克隆方法返回的是一个克隆对象,所以客户端代码要负责删除内存
    Prototype* clone() const override
    {
        return new ConcretePrototype1(*this);
    }
    void showMems() const override
    {
        cout << this->m_name << "  " << this->m_val << "  " << this->m_score << endl;
    }
};

//具象原型2
class ConcretePrototype2 :public Prototype
{
private:
    char m_ch; //声明其他的一些属性
public:
    ConcretePrototype2(string name, int val, char ch) :Prototype(name, val), m_ch(ch) {};
    //注意克隆方法返回的是一个克隆对象,所以客户端代码要负责删除内存
    Prototype* clone() const override
    {
        return new ConcretePrototype2(*this);
    }
    void showMems() const override
    {
        cout << this->m_name << "  " << this->m_val << "  " << this->m_ch << endl;
    }
};

//原型工厂,即原型注册器,通常包含所有可克隆的具体原型对象
class PrototypeFactory
{
private:
    unordered_map<int, Prototype*> m_prototypes;
public:
    //在创建原型工厂时,生成所有可克隆对象,之后客户端直接调用可克隆对象的clone方法获得新的对象
    PrototypeFactory()
    {
        m_prototypes[PROTOTYPE1] = new ConcretePrototype1("Prototype1",1,3.5);
        m_prototypes[PROTOTYPE2] = new ConcretePrototype2("Prototype2", 2, 'h');
    }
    //释放对应的内存
    ~PrototypeFactory()
    {
        for (auto mem : m_prototypes)
            delete mem.second;
    }
    //调用此函数获得一个具体原型对象,传入type控制输出类型
    Prototype* createPrototype(int type)
    {
        return m_prototypes[type]->clone();
    }
};

//客户端代码
void client(PrototypeFactory& factory)
{
    Prototype* type1 = factory.createPrototype(Type::PROTOTYPE1);
    type1->showMems();
    delete type1;
}


int main()
{
    PrototypeFactory* factory = new PrototypeFactory();
    client(*factory);
    delete factory;
    return 0;
}

 

posted @ 2020-08-11 22:08  晨枫1  阅读(537)  评论(0编辑  收藏  举报