设计模式(三)

Posted by KalosAner on December 20, 2024

9、构建器(Builder)

概念:将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。

问题:在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

动机:如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?

构建器模式

总结:类似模板方法模式,适用于流程稳定但流程细节可能变化的场景。

代码:

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
class House{
    //....
};

class HouseBuilder {
public:
    House* GetResult(){
        return pHouse;
    }
    virtual ~HouseBuilder(){}
protected:
    
    House* pHouse;
	virtual void BuildPart1()=0;
    virtual void BuildPart2()=0;
    virtual void BuildPart3()=0;
    virtual void BuildPart4()=0;
    virtual void BuildPart5()=0;
};

class StoneHouse: public House{
    
};

class StoneHouseBuilder: public HouseBuilder{
protected:
    
    virtual void BuildPart1(){
        //pHouse->Part1 = ...;
    }
    virtual void BuildPart2(){
        
    }
    virtual void BuildPart3(){
        
    }
    virtual void BuildPart4(){
        
    }
    virtual void BuildPart5(){
        
    }
};


class HouseDirector{
    
public:
    HouseBuilder* pHouseBuilder;
    
    HouseDirector(HouseBuilder* pHouseBuilder){
        this->pHouseBuilder=pHouseBuilder;
    }
    
    House* Construct(){
        
        pHouseBuilder->BuildPart1();
        
        for (int i = 0; i < 4; i++){
            pHouseBuilder->BuildPart2();
        }
        
        bool flag=pHouseBuilder->BuildPart3();
        
        if(flag){
            pHouseBuilder->BuildPart4();
        }
        
        pHouseBuilder->BuildPart5();
        
        return pHouseBuilder->GetResult();
    }
};

int main() {
	HouseBuilder* stoneHouseBuilder = new StoneHouseBuilder();
	HouseDirector* stoneHouseDirector = new HouseDirector(stoneHouseBuilder);
	House* stoneHouse = stoneHouseDirector->Construct
}

10、单例模式(Singleton)

概念:保证一个类仅有一个实例,并提供一个该实例得全局访问点。

问题:在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们得逻辑正确性,以及良好的效率。

动机:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?

单例模式

总结:解决性能问题,确保一个类仅被创建一个实例。

代码:

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
class Singleton{
private:
    Singleton();
    Singleton(const Singleton& other);
public:
    static Singleton* getInstance();
    static Singleton* m_instance;
};

Singleton* Singleton::m_instance=nullptr;

//线程非安全版本
Singleton* Singleton::getInstance() {
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}

//线程安全版本,但锁的代价过高
Singleton* Singleton::getInstance() {
    Lock lock;
    if (m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}

//双检查锁,但由于内存读写reorder不安全
// reorder是指高级代码在编译阶段,编译器会对代码进行优化可以能会导致指令顺序变化
// 如代码:m_instance = new Singleton(); 在不优化的情况下,指令执行顺序是
// 1、new分配内存,2、调用构造函数,3、把这块内存赋值给m_instance,
// 但是如果编译器进行优化顺序可能变成:1、new分配内存,2、把这块内存赋值给m_instance,3、调用构造函数,
// 在单线程下即便优化也不会出错,但是在多线程的情况下,可能会出现以下错误:
// 如果线程A刚执行到第2个指令,此时m_instance的值已经不为nullptr了,但是还没有调用构造函数,此时该变量是不可用的,
// 但是如果线程B执行第一个if(m_instance==nullptr)就会导致发现m_instance不等于nullptr
// 就会直接返回m_instance,导致返回一个错误的对象
Singleton* Singleton::getInstance() {
    
    if(m_instance==nullptr){
        Lock lock;
        if (m_instance == nullptr) {
            m_instance = new Singleton();
        }
    }
    return m_instance;
}

//C++ 11版本之后的跨平台实现 (volatile)
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
    if (tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release);//释放内存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}

11、享元模式(Flyweight)

概念:运用共享技术有效地支持大量细粒度地对象。

问题:在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。

动机:如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象地方式来进行操作?

享元模式

总结:解决性能问题,使用对象共享降低对象个数。

代码:

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
class Font {
private:

    //unique object key
    string key;
    
    //object state
    //....
    
public:
    Font(const string& key){
        //...
    }
};

class FontFactory{
private:
    map<string, Font* > fontPool;
    
public:
    Font* GetFont(const string& key){

        map<string,Font*>::iterator item=fontPool.find(key);
        
        if(item!=footPool.end()){
            return fontPool[key];
        }
        else{
            Font* font = new Font(key);
            fontPool[key]= font;
            return font;
        }

    }
    
    void clear(){
        //...
    }
};

12、门面模式(Façade)

概念:为子系统中的一组接口提供一个一致(稳定)的界面,Façade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用(复用)。

问题:如果组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临着很多变化的挑战。

动机:如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦?

外观模式

总结:添加中间层。

代码:

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
class Shape {
    virtual void draw();
    virtual ~Shape(){}
}
class Rectangle : public Shape {
public:
    void draw() {
        cout << "Rectangle::draw()" << endl;
    }
}
class Square : public Shape {
public:
    void draw() {
        cout << "Square::draw()" << endl;
    }
}
class Circle : public Shape {
public:
    void draw() {
        cout << "Circle::draw()" << endl;
    }
}
class ShapeMaker {
private:
    Shape circle;
    Shape rectangle;
    Shape square;
 
public:
    ShapeMaker() {
        circle = new Circle();
        rectangle = new Rectangle();
        square = new Square();
    }
    void drawCircle(){
        circle.draw();
    }
	void drawRectangle(){
        rectangle.draw();
    }
    void drawSquare(){
        square.draw();
    }
}