设计模式(一)

设计模式概论

为什么要学习设计模式

众所周知,面向对象编程(OOP)是编程中的一个重要思想。基于此,计算机学界诞生了一大批术语:类、继承、多态、虚方法等,一大批面向对象的语言C++javaC#也开始大放异彩。

目前大多数工科院系都开设面向对象编程(C++)课程。不少同学在学习这门课时也许会遇到和我一样的疑惑:我懂得类、继承、虚函数的使用规则,但是这样做到底有什么实在的好处呢?如果仅仅学习C++的语法,我们是不能深刻体会面向对象的优越性的,这样学到的东西只能称之为"C with class"。而要想真正理解面向对象,就需要进一步了解设计模式(相关内容只有在软件工程的相关课程中才有讲授)。

要想从一个编程新手进阶为有一定编程思维的程序员,无法绕开两项内容:算法和设计模式。算法的重要性自不必多说,而了解设计模式,既可以让你在开发工程时更加条理、更容易与他人协作,又可以增强阅读他人所写源代码的能力。

设计模式种类繁多,不便记忆。为了便于大家理解每一项设计模式,博主特意为每一种设计模式都配备了一个独特的例子。例如,建造者模式可以理解为“具有不同食材配比的菜谱”,而桥接模式可以理解为“在不同的操作系统上玩不同的游戏”。在实际编写代码时,也可结合对应的例子,权衡每一种设计模式的优劣。

本人才疏学浅,若有谬误,欢迎指出!

说明:博客内容为博主学习设计模式时所做的笔记,例子力求精简、易于理解。在创作过程中主要参考了以下网站:

https://refactoringguru.cn/ 这是一名乌克兰裔程序员亚历山大·什韦茨所创立的网站。网站详尽地解释了各个设计模式,并提供了多种面向对象编程语言的实例。不需要购买电子书,因为网站内容已经十分详尽。

https://blog.csdn.net/sinat_21107433/category_9418696.html

设计模式的原则

所有的设计模式都遵循以下几个原则:

  1. 开闭原则**:**对扩展开放,对修改关闭
  2. 里氏代换原则:任何基类可以出现的地方,子类一定可以出现
  3. 依赖倒转原则:对接口编程
  4. 接口隔离原则:使用多个隔离的接口,比使用单个接口要好
  5. 迪米特法则:一个实体应当尽量少地与其他实体之间发生相互作用
  6. 合成复用原则:尽量使用合成/聚合的方式,而不是使用继承

设计模式分类

1.单例模式

定义

单例模式就是一个类只能被实例化一次 ,更准确的说是只能有一个实例化的对象的类。

  • 这个类只能有一个实例(只有在指针为空时创建新对象,否则返回原有对象);
  • 它必须自己创建这个实例(构造函数为私有,然后通过getInstance方法间接访问);
  • 它必须自己向整个系统提供这个实例(getInstance为静态成员函数)。

注意:若为多线程环境,创建实例时需要用互斥锁加以保护。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <mutex>
class Singleton
{
public:
    // 获取单例对象的函数,注意实例对象必须是全局的
    static Singleton *getInstance(int num)
    {
        // m_mutex.lock();
        if (instance == nullptr)
        {
            std::cout << "new instance!" << std::endl;
            instance = new Singleton(num);
        }
        // m_mutex.unlock();
        return instance;
    }
    // 严谨起见,单例模式下对象既不能够被赋值,也不能够被复制
    Singleton(Singleton &other) = delete;
    void operator=(const Singleton &) = delete;
    int getNum() const
    {
        return this->num;
    }

private:
    // 私有的构造函数,这里的构造函数不能直接使用
    Singleton(int num)
    {
        this->num = num;
    }
    // 私有字段
    int num;
    // 静态方法,便于全局访问实例
    static Singleton *instance;
    // static std::mutex m_mutex;
};

// 初始值赋为nullptr
Singleton *Singleton::instance = nullptr;
// std::mutex Singleton::m_mutex;


int main()
{
    Singleton *s1 = Singleton::getInstance(1);
    std::cout << s1->getNum() << std::endl;
    // 此后将不再创建新的实例
    Singleton *s2 = Singleton::getInstance(2);
    std::cout << s2->getNum() << std::endl;
}

结果:

new instance! 1 1

2.工厂模式

定义

定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。

有4种成员:抽象工厂、具体工厂、抽象产品、具体产品。

以下以“生产手机和电脑的工厂”为例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <iostream>
// 抽象产品
class AbstractProduct
{
public:
    AbstractProduct() {}
    virtual ~AbstractProduct() {}
    virtual void getInfo() const = 0;
};

// 具体产品1
class Computer : public AbstractProduct
{
public:
    Computer(int price) : price(price)
    {
        getInfo();
    }
    virtual void getInfo() const override
    {
        std::cout << "computer:$" << price << std::endl;
    }

private:
    int price;
};

// 具体产品2
class MobilePhone : public AbstractProduct
{
public:
    MobilePhone(int price) : price(price)
    {
        getInfo();
    }
    virtual void getInfo() const override
    {
        std::cout << "mobile phone:$" << price << std::endl;
    }

private:
    int price;
};

// 抽象工厂
class AbstractFactory
{
public:
    virtual AbstractProduct *getProduct(int) const = 0; // 返回产品基类指针,利用多态得到不同的产品
    virtual ~AbstractFactory() {}
};

// 具体工厂1
// 每引入一个新的产品,就要创建一个对应的工厂(重写子类)
// 若构造方法较为简单,也可将抽象工厂和具体工厂合并,用switch-case语句实现
class ComputerFactory : public AbstractFactory
{
public:
    AbstractProduct *getProduct(int price) const override
    {
        return new Computer(price);
    }
};

// 具体工厂2
class MobilePhoneFactory : public AbstractFactory
{
public:
    AbstractProduct *getProduct(int price) const override
    {
        return new MobilePhone(price);
    }
};

int main()
{
    AbstractFactory *fac = nullptr;

    fac = new ComputerFactory();
    fac->getProduct(1000);
    delete fac;

    fac = new MobilePhoneFactory();
    fac->getProduct(500);
    delete fac;
}

结果:

computer:$1000 mobile phone:$500

3.抽象工厂模式

定义

提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。

与一般工厂模式不同的是,抽象工厂模式的每一个具体工厂可以生产多种产品。这多种产品一般不属于同一个类别,但相互依存、配套、紧密相关。

以下以“生产手机和电脑及其配套的USB的工厂”为例:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <iostream>
// 抽象产品类1(此处为生产电子产品本体)
class AbstractProductA
{
public:
    AbstractProductA() {}
    virtual ~AbstractProductA() {}
    virtual void getInfo() const = 0;
};

// 抽象产品类2(此处为生产电子产品配套的USB)
class AbstractProductB
{
public:
    AbstractProductB() {}
    virtual ~AbstractProductB() {}
    virtual void getInfo() const = 0;
};

// 具体产品1(属于类A)
class Computer : public AbstractProductA
{
public:
    Computer(int price) : price(price)
    {
        getInfo();
    }
    virtual void getInfo() const override
    {
        std::cout << "computer:$" << price << std::endl;
    }

private:
    int price;
};

// 具体产品2(属于类A)
class MobilePhone : public AbstractProductA
{
public:
    MobilePhone(int price) : price(price)
    {
        getInfo();
    }
    virtual void getInfo() const override
    {
        std::cout << "mobile phone:$" << price << std::endl;
    }

private:
    int price;
};

// 具体产品3(属于类B)
class ComputerUSB :public AbstractProductB
{
public:
    ComputerUSB(int price):price(price)
    {
        getInfo();
    }
    virtual void getInfo() const override
    {
        std::cout << "computer USB:$" << price << std::endl;
    }

private:
    int price;
};

// 具体产品4(属于类B)
class MobilePhoneUSB :public AbstractProductB
{
public:
    MobilePhoneUSB(int price):price(price)
    {
        getInfo();
    }
    virtual void getInfo() const override
    {
        std::cout << "mobile phone USB:$" << price << std::endl;
    }

private:
    int price;
};


// 抽象工厂
class AbstractFactory
{
public:
    virtual AbstractProductA *getProductA(int) const = 0 ; // 返回产品基类指针,利用多态得到不同的产品
    virtual AbstractProductB *getProductB(int) const = 0 ; // 可以生产多个配套的产品
    virtual ~AbstractFactory() {}
};

// 具体工厂1
// 每引入一个新的产品,就要创建一个对应的工厂(重写子类)
class ComputerFactory : public AbstractFactory
{
public:
    AbstractProductA *getProductA(int price) const override
    {
        return new Computer(price);
    }
    AbstractProductB *getProductB(int price) const override
    {
        return new ComputerUSB(price);
    }
};

// 具体工厂2
class MobilePhoneFactory : public AbstractFactory
{
public:
    AbstractProductA *getProductA(int price) const override
    {
        return new MobilePhone(price);
    }
    AbstractProductB *getProductB(int price) const override
    {
        return new MobilePhoneUSB(price);
    }
};

int main()
{
    AbstractFactory *fac = nullptr;

    fac = new ComputerFactory();
    std::cout<<"produce a computer:"<<std::endl;
    fac->getProductA(1000);
    fac->getProductB(50);
    delete fac;

    std::cout<<"produce a mobile phone:"<<std::endl;
    fac = new MobilePhoneFactory();
    fac->getProductA(500);
    fac->getProductB(30);
    delete fac;
}

4.原型模式

定义

使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。

原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。原型模式可以免于考虑浅拷贝和深拷贝。

以下以“获取不同颜色和形状的图形”为例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <cstring>
// 基类,需要将Clone方法定义为虚方法
class Shape
{
public:
    Shape(int centerX, int centerY, char *_color) : centerX(centerX), centerY(centerY), color(_color)
    {
        
    }
    virtual Shape *Clone() const = 0;
    virtual void getAddr() const = 0;
    virtual ~Shape()
    {
        
    }

protected:
    int centerX, centerY;
    char *color;
};

class Circle : public Shape
{
public:
    Circle(int centerX, int centerY, char *_color, int radius) : Shape(centerX, centerY, _color), radius(radius)
    {
        std::cout << "Circle:(" << centerX << ',' << centerY << ")," << radius << ',' << _color << std::endl;
    }
    // clone的基本操作
    Shape *Clone() const override
    {
        return new Circle(*this);
    }
    void getAddr() const override
    {
        std::cout << &centerX << ' ' << &centerY << ' ' << &radius << ' ' << &color << std::endl;
    }

private:
    int radius;
};

class Square : public Shape
{
    Square(int centerX, int centerY, char *_color, int x) : Shape(centerX, centerY, _color), x(x)
    {
        std::cout << "Square:(" << centerX << ',' << centerY << ")," << x << ',' << _color << std::endl;
    }
    Shape *Clone() const override
    {
        return new Square(*this);
    }
    void getAddr() const override
    {
        std::cout << &centerX << ' ' << &centerY << ' ' << &x << ' ' << &color << std::endl;
    }

private:
    int x;
};

int main()
{
    Shape *shape1 = new Circle(2, 2, "red", 3);
    Shape *shape2 = shape1->Clone();
    // 可以看到,调用clone方法可以避免浅拷贝的问题
    shape1->getAddr();
    shape2->getAddr();
    delete shape1;
    delete shape2;
}

结果:

Circle:(2,2),3,red 0xd714d8 0xd714dc 0xd714e8 0xd714e0 0xd71528 0xd7152c 0xd71538 0xd71530

5.建造者模式

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

有4种成员:抽象生成器(实现构造方法和装配方法的抽象接口)、具体生成器(实现各个部件的具体构造方法和装配方法)、产品、主管(隔离客户与对象的生产过程,并负责控制产品对象的生产过程)。

以下以“烹饪两种不同菜品的厨师”为例:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <iostream>
#include <utility>
#include <string>
typedef std::pair<std::string,int> formula; // 菜谱

// 产品类(此处为饭菜)
class Dish
{
public:
    // 在建造者模式中,不需要传统的构造函数形式
    Dish(){}
    void setMeat(formula meat)
    {
        this->meat = meat;
    }
    void setVegetable(formula vegetable)
    {
        this->vegetable = vegetable;
    }
    void getInfo()
    {
        std::cout << meat.first << ':' << meat.second << 'g' << std::endl;
        std::cout << vegetable.first << ':' << vegetable.second << 'g' << std::endl;
    }

private:
    formula meat;
    formula vegetable;
};

// 抽象建造者类
class AbstractBuilder
{
public:
    AbstractBuilder()
    {
        this->dish = new Dish();
    }
    virtual void putMeat() = 0;
    virtual void putVegetable() = 0;
    virtual Dish* getDish() = 0;
    virtual ~AbstractBuilder(){}
protected:
    Dish *dish;
};

// 具体建造者类1(菜谱1)
class ConcreteBuilderA :public AbstractBuilder
{
public:
    void putMeat () override
    {
        this->dish->setMeat(std::make_pair("pork",50));
    }
    void putVegetable () override
    {
        this->dish->setVegetable(std::make_pair("onion",100));
    }
    Dish* getDish() override
    {
        return this->dish;
    }
};

// 具体建造者类2(菜谱2)
class ConcreteBuilderB :public AbstractBuilder
{
public:
    void putMeat () override
    {
        this->dish->setMeat(std::make_pair("beef",100));
    }
    void putVegetable () override
    {
        this->dish->setVegetable(std::make_pair("potato",200));
    }
    Dish* getDish() override
    {
        return this->dish;
    }
};

// 主管类,负责封装建造流程,返回建造结果
class Director
{
public:
    Director(){}
    void setBuilder(AbstractBuilder *_Builder)
    {
		this->builder = _Builder;
	}
    // 核心封装代码
    Dish* constructor()
    {
        this->builder->putMeat();
        this->builder->putVegetable();
        return this->builder->getDish();
    }
private:
	AbstractBuilder *builder;
};

int main()
{
    AbstractBuilder *builder;
	Director *director = new Director();
    Dish *dish;

    // 指定建造器A
    std::cout<<"dish1:"<<std::endl;
    builder = new ConcreteBuilderA();
    director->setBuilder(builder);
    dish = director->constructor();
    dish->getInfo();

    // 指定建造器B
    std::cout<<"dish2:"<<std::endl;
    builder = new ConcreteBuilderB();
    director->setBuilder(builder);
    dish = director->constructor();
    dish->getInfo();

    delete builder;
    delete director;
    delete dish;
}

结果:

dish1: pork:50g onion:100g dish2: beef:100g potato:200g

Built with Hugo
Theme Stack designed by Jimmy
visitors: total visits: time(s) reads: time(s)