设计模式汇总

Posted by Bilthas on June 29, 2024

前言

感觉设计模式就像大学里的傅里叶变换一样,仿佛每门课都能沾上一点,但一直都没很清晰。这可能是因为学校做的实验玩具往往都很小型,往往也不需要考虑多少健壮性、扩展性之类的问题,导致实践中用的极少。提起设计模式总感觉还差点意思,本篇记录一些常用的设计模式和自己的理解。

单例模式Singleton

单例模式顾名思义,就是只有一个实例。写程序的过程中,我们有时可能会希望某一个类别的东西有且只能存在一个,一般有这种需要的可能是一些共享资源,比如数据库或者文件。要求第一个用户使用它和第二个用户使用它用的其实都是一个东西。也为了更加严格的控制该全局变量,把单例用到全局变量上去,这样某个全局变量就存在那么一个,其他人也不能新建一个,访问也只有这一个。

那么为了能够达成上述的这种效果,主要要做到两点:
1.保证一个类只有一个实例。
2.为该实例提供一个全局访问节点:因为访问的方式由设计者来提供,才能有效保证这个实例是唯一的,相当于封锁了控制的权限。

实现方式

1.默认的构造函数设为私有,防止其他对象使用单例类的new运算符,控制住添加实例的权限。
2.新建一个与之相关静态的构建方法作为构造函数(如果不提供入口,就成了纯私有变量了),在这个自定义的构造函数中,如果是第一次新建,那么就调用默认的构造方法。而如果已经存在了,那么有人再调用这个构造,就只是把原有的返回,防止新建实例。
3.考虑线程安全问题。

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
class Singleton
{
private:
    static Singleton* singleton_;
    static std::mutex mutex_;

protected:
    Singleton(const std::string value): value_(value)
    {
    }
    ~Singleton() {}
    std::string value_;
public:
    // 单例应该禁止拷贝
    Singleton(Singleton &other) = delete;
    // 禁止赋值
    void operator=(const Singleton &) = delete;
    static Singleton *GetInstance(const std::string &value);
    void SomeBusinessLogic()
    {
        // ...  
    }
    std::string value() const{
        return value_;
    }
};

Singleton* Singleton::singleton_ = nullptr;
std::mutex Singleton::mutex_;

Singleton *Singleton::GetInstance(const std::string &value)
{
    // 线程加锁
    std::lock_guard<std::mutex> lock(mutex_);
    if(singleton_ == nullptr){
        singleton_ = new Singleton(value);
    }
    return singleton_;
}

void ThreadFoo(){
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    Singleton* singleton = Singleton::GetInstance("FOO");
    std::cout << singleton->value() << std::endl;
}

void ThreadBar(){
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    Singleton* singleton = Singleton::GetInstance("BAR");
    std::cout << singleton->value() << "\n";
}

int main()
{
    /**
     * 这里如果输出相同的值,那么单例被重用了
     * 如果看到不同的值,就创建了两个单例,出现错误了
     * 涉及到线程安全时,要加锁
     */
    std::thread t1(ThreadFoo);
    std::thread t2(ThreadBar);
    t1.join();
    t2.join();
    return 0;
}

More

https://refactoringguru.cn/design-patterns/catalog 这个网站写的很好