设计模式:观察者模式、工厂模式、单例模式

设计模式

设计模式:是指在软件开发中,经过验证的,用于解决特定环境下、重复重现的、特定问题的解决方案
我们要注意:不要为了套用设计模式而使用设计模式,在业务遇到问题时,要自然而然想到设计模式作为一种解决方案

为什么要学设计模式

  • 设计模式已经成为软件开发人员的标准词汇
  • 学习设计模式是个人技术能力提高的途径
  • 不用重复造轮子
  • 各种源码当中充斥着各种设计模式

观察者模式

定义对象间的一种一对多(变换)的依赖关系,以便当一个对象(subject)的状态发生改变时,所有依赖于它的对象都得到通知并且自动更新

使用场景

设计模式:观察者模式、工厂模式、单例模式_第1张图片
当用户订阅了某种消息,当消息有改变的时候,就会通知用户消息状态的改变,并且执行用户对应消息改变所需要的行为
要点

  1. 使用面向对象的抽象,observer模式使我们可以独立的改变目标与观察者,从而使得两者之间的依赖可以达到松耦合。
  2. 目标指定发送通知时,无需指定观察者,通知会自动传播
  3. 观察者可以决定是否需要订阅通知,目标对象对此一无所知。

推模式和拉模式

  1. 推模式: 目标对象向观察者推送目标的详细信息,不管观察者是否需要,相当于计算机网络中的广播。
  2. 拉模式: 目标在通知观察者的时候只传递少量信息。如果观察者需要更详细的信息,应该是观察者自身向目标对象获取。

源码

#include 
#include 
#include 
using namespace std;
class Subject;

class Observer
{
public:
    Observer() {}
    virtual ~Observer() {}
    virtual void update(Subject *sj) = 0;
    virtual void update(string content) = 0;
};
class Subject
{
public:
    Subject() {}
    virtual ~Subject() {}
    virtual string getcontent() = 0;
    virtual string getAbstractContent() = 0;
    void attach(Observer *ob)
    {
        observers.push_back(ob);
    }
    void detach(Observer *ob)
    {
        observers.remove(ob);
    }
    virtual void notifyObservers()
    {
        for (Observer *ob : observers)
        {
            ob->update(this); //拉模型,具体数据让观察者自己去取
        }
    }
    virtual void notifyObservers(string content)
    {
        for (Observer *ob : observers)
        {
            ob->update(content); //推模型 数据是由被观察者选择
        }
    }

private:
    list observers;
};

class Reader : public Observer
{
public:
    Reader(string name) : _readername(name) {}
    virtual ~Reader() {}
    virtual void update(Subject *sj)
    {
        cout << _readername << " 开始阅读整个" << sj->getcontent() << endl;
    }
    virtual void update(string content)
    {
        cout << _readername << " 开始阅读报纸简介" << endl;
    }

private:
    string _readername;
};

class paper : public Subject
{
public:
    paper() {}
    virtual ~paper() {}

    void setcontent(string content)
    {
        this->content = content;
    }

    virtual string getcontent()
    {
        return content;
    }
    virtual string getAbstractContent()
    {
        return "摘要";
    }

private:
    string content;
};
int main()
{
    paper newpaper;
    newpaper.setcontent("今日头条");
    Reader read1("user1"), read2("user2");
    newpaper.attach(&read1); //订阅
    newpaper.attach(&read2); //订阅
    newpaper.notifyObservers();
    return 0;
}

工厂模式

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。

动机

  • 在软件系统中,经常面临创建对象的工作。由于需求的变换,需要创建的对象的具体类型经常变换。
  • 如何应对这种变化?如何绕过常规的对象创建方法,提供一种"封装机制"来避免客户程序和这种"具体对象创建工作"的紧耦合。

使用场景

  • 数据导出各种格式
  • 支付接口,可能对应不同的支付网关:支付宝,财付通,网银在线等等。

要点总结

  • factory method模式用于隔离对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系会导致软件的脆弱
  • factory method模式通过面对对象的方式,将所要创建的具体对象工作延迟到子类,从而实现一种扩展的策略,较好的解决了耦合关系。
  • factory method模式解决了单个模式的需求变化。缺点在于要求创建方式/参数相同。

代码

#include 

using namespace std;
class ExportFileProduct
{
public:
    ExportFileProduct() {}
    virtual ~ExportFileProduct() {}
    virtual bool Export(string data) = 0;
};
class ExportTextProduct : public ExportFileProduct
{
public:
    ExportTextProduct() {}
    virtual ~ExportTextProduct(){};
    virtual bool Export(string data)
    {
        cout << "导出数据[" << data << "]保存成文本的方式" << endl;
        return true;
    }
};
class ExportDBProduct : public ExportFileProduct
{
public:
    ExportDBProduct() {}
    virtual ~ExportDBProduct(){};
    virtual bool Export(string data)
    {
        cout << "导出数据[" << data << "]保存成数据库的方式" << endl;
        return true;
    }
};
class ExportJsonProduct : public ExportFileProduct
{
public:
    ExportJsonProduct() {}
    virtual ~ExportJsonProduct() {}
    virtual bool Export(string data)
    {
        cout << "导出数据:[" << data << "]保存Json的方式" << endl;
        return true;
    }
};

class ExportFactory
{
public:
    ExportFactory() {}
    virtual ~ExportFactory() {}
    bool Export(int type, string data)
    {
        ExportFileProduct *product = factoryMethod(type);
        bool ret = false;
        if (product)
        {
            ret = product->Export(data);
        }
        else
        {
            cout << "没有对应的类型" << endl;
        }
        return ret;
    }

protected:
    virtual ExportFileProduct *factoryMethod(int type)
    {
        ExportFileProduct *product;
        if (type == 1)
        {
            product = new ExportTextProduct();
        }
        else if (type == 2)
        {
            product = new ExportDBProduct();
        }
        else if (type == 3)
        {
            product = new ExportJsonProduct();
        }
        return product;
    }
};

单例模式

保证一个类只有一个实例,并提供一个该实例的全局访问点

动机

  • 在软件系统中,经常有这样一些特殊的类,必须确保他们在系统中只存在一个实例,才能确保他们的逻辑正确性,以及良好的效率。
  • 绕过常规的构造器,提供一种机制来保证一个类只有一个实例。
  • 类设计者的责任 而不是使用者的责任

常见写法

饿汉式单例

程序开始运行时就创建单例

class Singleton4
{
private:
    Singleton4() = default;
    Singleton4(const Singleton4 &s) = delete;
    Singleton4 &operator=(Singleton4 &s) = delete;
    static Singleton4 _singleton;

private:
    static Singleton4 *getinstance()
    {
        return &_singleton;
    }
};
Singleton4 Singleton4::_singleton;
懒汉式单例

使用单例时才开始创建

双锁型单例模式
/* 双检查锁,但由于内存读写reorder不安全 因为C++创建对象时,会执行1、分配内存,2 调用构造,3 赋值操作三步操作,
然而现代CPU和编译器高并发下可能会进行乱序重排操作,因而创建对象new CSingleton的第2步可能会晚于第3步进行指令调用,
因而导致出现未定义的的行为。*/
class Singleton3
{
private:
    static Singleton3 *_singleton;
    static mutex _mutex;
    Singleton3() = default;
    Singleton3(const Singleton3 &s) = delete;
    Singleton3 &operator=(const Singleton3 &s) = delete;
    class GarbageCollector
    {
    public:
        ~GarbageCollector()
        {
            cout << "~GarbageCollector()\n";
            if (Singleton3::_singleton)
            {
                cout << "free singleton";
                delete Singleton3::_singleton;
                Singleton3::_singleton = nullptr;
            }
        }
    };
    static GarbageCollector _gc; //模拟gc来回收单例

public:
    static Singleton3 *getinstance()
    {

        //Singleton* tmp = m_instance.load(std::memory_order_relaxed);
        //std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence 可以使得高并发下不会出现内存读写reorderdd
        if (_singleton == nullptr)
        {
            _mutex.lock(); //对象的new不是原子操作 1、分配内存,2 调用构造,3 赋值操作,到第3步的时候才是m_singleton非空
            //  1、分配内存,2 赋值操作 3 调用构造,到第2步的时候才是m_singleton非空
            if (_singleton == nullptr)
            {
                _singleton = new Singleton3();
            }
            _mutex.unlock();
        }
        return _singleton;
    }
};
Singleton3 *Singleton3::_singleton = nullptr;
Singleton3::GarbageCollector Singleton3::_gc;
mutex Singleton3::_mutex;

在高并发下双锁型单例模式可能由于内存读写reorder造成隐患

线程安全型单例
class Singleton2
{
private:
    static Singleton2 *_singleton;
    static mutex _mutex;
    Singleton2() = default;
    Singleton2(const Singleton2 &s) = delete;
    Singleton2 &operator=(const Singleton2 &s) = delete;
    class GarbageCollector
    {
    public:
        ~GarbageCollector()
        {
            cout << "~GarbageCollector()\n";
            if (Singleton2::_singleton)
            {
                cout << "free singleton";
                delete Singleton2::_singleton;
                Singleton2::_singleton = nullptr;
            }
        }
    };
    static GarbageCollector _gc; //模拟gc来回收单例

public:
    static Singleton2 *getinstance()
    {
        _mutex.lock(); // 加锁的粒度大,效率较低, 对高并发的访问
        if (_singleton == nullptr)
        {
            _singleton = new Singleton2();
        }
        _mutex.unlock();
        return _singleton;
    }
};
Singleton2 *Singleton2::_singleton = nullptr;
Singleton2::GarbageCollector Singleton2::_gc;
mutex Singleton2::_mutex;

加锁之后在并发高的场景,效率很低,一般不推荐这种写法

线程不安全懒汉式
class Singleton1
{
private:
    static Singleton1 *_singleton;
    Singleton1() = default;
    Singleton1(const Singleton1 &s) = delete;
    Singleton1 &operator=(const Singleton1 &s) = delete;
    class GarbageCollector
    {
        //线程不安全
    public:
        ~GarbageCollector()
        {
            cout << "~GarbageCollector()\n";
            if (Singleton1::_singleton)
            {
                cout << "free singleton";
                delete Singleton1::_singleton;
                Singleton1::_singleton = nullptr;
            }
        }
    };
    static GarbageCollector _gc; //模拟gc来回收单例

public:
    static Singleton1 *getinstance()
    {
        if (_singleton == nullptr)
        {
            _singleton = new Singleton1();
        }
        return _singleton;
    }
};
Singleton1 *Singleton1::_singleton = nullptr;
Singleton1::GarbageCollector Singleton1::_gc;
局部变量的懒汉式

推荐写法

class Singleton
{
private:
    Singleton(){};
    Singleton(const Singleton &s) = delete;
    Singleton &operator=(Singleton &s) = delete;

public:
    static Singleton *getinstance()
    {
        static Singleton _singleton;
        return &_singleton;
    }
};

有一些坑,如果使用隐式构造函数导致局部静态变量不安全,读者可以自行编译成汇编 看看该局部变量有没有加锁

要点总结
  • singleton模式中实例构造器可以设置为protected以允许子类派生
  • singleton模式一般不要支持拷贝构造函数和clone接口,因为有可能导致多个实例对象,与singleton模式的初衷违背。
  • 隐式构造函数导致函数局部静态变量不安全。

你可能感兴趣的:(设计模式)