设计模式-C++

0. 设计模式概述

0.1、定义

设计模式就是经由无数前人的实践,在设计过程中能够反复使用、解决特定问题的设计方法。(针对设计问题的通用解决方案)

0.2、分类

1、创建型模式,5种

工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

2、结构型模式,7种

适配器模式、装饰器模式、外观模式、桥接模式、组合模式、享元模式、代理模式

3、行为型模式,11种

策略模式、模版方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

0.3、为什么适用设计模式

可以把它应用到特定的应用中,用于解决相似的问题。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

0.4、面向对象程序设计(OOP)的设计模式的五大原则

1、单一职责原则

英文名称是Single Responsibility Principle,简称SRP。定义为就一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数、数据的封装。

单一职责有2个含义,一个是避免相同的职责分散到不同的类中,另一个是避免一个类承担太多职责。减少类的耦合,提高类的复用性。

2、接口隔离原则

英文全称是InterfaceSegregation Principles,简称ISP。使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

每个接口所要的实现的功能应该要最小化,只要定义出自己仅需的接口函数就行。
简单说,就是使用多个专门的接口比使用单个接口好很多。

3、开放-封闭原则

英文全称是Open Close Principle,简称OCP。一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。 即软件实体应尽量在不修改原有代码的情况下进行扩展。

软件的实现完美情况。大概就是说在实现新功能的实话,不需要修改你的代码,只需要拓展代码来实现新的功能。

核心思想就是对抽象编程,而不对具体编程。

4、替换原则

所有引用基类的地方必须能透明地使用其子类的对象。

只要 父类 能出现的地方 子类 就可以出现,而且替换为 子类 也不产生任何异常错误,反之则不然。这主要体现在,我们经常使用抽象类/基类做为方法参数,具体使用哪个子类作为参数传入进去,由调用者决定。

主要针对继承的设计原则

1)子类必须完全实现父类的方法,子类可以有自己的个性外观(属性)和行为(方法)

2)在客户端程序中只应该使用父类对象而不应当直接使用子类对象,这样可以实现运行期间绑定。

5、依赖倒置原则

抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。 关键点: 1. 高层模块不应该依赖低层模块,两者都应该依赖其抽象 2. 抽象不应该依赖细节 3. 细节应该依赖抽象

上层模块不应该依赖于下层模块,他们共同依赖于一个抽象,即:父类不能依赖子类,他们都要依赖抽象类。

抽象不能依赖于具体,具体应该要依赖于抽象。

0.5、Rereference

1. 单例模式

参考
在这里插入图片描述

作用:单例模式主要解决一个全局使用的类频繁的创建和销毁的问题。保证一个类只有一个实例,并提供一个访问它的全局访问点,使得系统中只有唯一的一个对象实例。实现单例模式必须注意一下几点:

  • 单例类只能由一个实例化对象。

  • 单例类必须自己提供一个实例化对象。

  • 单例类必须提供一个可以访问唯一实例化对象的接口。

应用:常用于管理资源,如日志、线程池

实现要点

  • 在类中,要构造一个实例,就必须调用类的构造函数,并且为了保证全局只有一个实例, 需防止在外部调用类的构造函数而构造实例,需要将构造函数的访问权限标记为private

  • 同时阻止拷贝创建对象时赋值时拷贝对象,因此也将它们声明并权限标记为private;

  • 需要提供一个全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例。

class Singleton{
public:
	static Singleton& getInstance(){
		static Singleton instance;
		return instance;
	}

void printTest(){
	cout<<"do something"<<endl;
}

private:
	Singleton(){}//防止外部调用构造创建对象
	Singleton(Singleton const &singleton);//阻止拷贝创建对象
	Singleton& operator=(Singleton const &singleton);//阻止赋值对象
};

int main()
{
	Singleton &a=Singleton::getInstance();
	a.printTest();
	return 0;
}

(1)首先,构造函数声明成private的目的是只允许内部调用,getInstance()中的静态局部变量创建时调用,但不允许外部调用构造创建第二个实例;
(2)然后,拷贝构造和拷贝赋值符是声明成了private而不给出定义,其目的是阻止拷贝,如果企图通过拷贝构造来创建第二个实例,编译器会报错。

阻止拷贝的另一种写法是声明后接一个"=delete",也能起到相同的作用(C++11)。
在这里插入图片描述

单例模式分为懒汉和饿汉两种实现方式。

懒汉模式:在getinstance中实例化

饿汉模式:在单例类定义时实例化

1.1、懒汉单例模式

懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化一个对象。在访问量较小,甚至可能不会去访问的情况下,采用懒汉实现,这是以时间换空间。

1.1.1、非线程安全的懒汉单例模式
/*
* 关键代码:构造函数是私有的,不能通过赋值运算,拷贝构造等方式实例化对象。
*/

//懒汉式一般实现:非线程安全,getInstance返回的实例指针需要delete
class Singleton
{
public:
    static Singleton* getInstance();
    ~Singleton(){}
private:
    Singleton(){}                                        //构造函数私有
    Singleton(const Singleton& obj) = delete;            //明确拒绝
    Singleton& operator=(const Singleton& obj) = delete; //明确拒绝

    static Singleton* m_pSingleton;
};

Singleton* Singleton::m_pSingleton = NULL;
Singleton* Singleton::getInstance()
{
    if(m_pSingleton == NULL)
        m_pSingleton = new Singleton;
    return m_pSingleton;
}

1.1.2、线程安全的懒汉单例模式–双重锁机制
std::mutex mt;

class Singleton
{
public:
    static Singleton* getInstance();
private:
    Singleton(){}                                    //构造函数私有
    Singleton(const Singleton&) = delete;            //明确拒绝
    Singleton& operator=(const Singleton&) = delete; //明确拒绝
    static Singleton* m_pSingleton;
};

Singleton* Singleton::m_pSingleton = NULL;
Singleton* Singleton::getInstance()
{
    if(m_pSingleton == NULL)
    {
        mt.lock();
        if(m_pSingleton == NULL)
            m_pSingleton = new Singleton();
        mt.unlock();
    }
    return m_pSingleton;
}

1.2、饿汉模式

饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。

饿汉模式不需要用锁,就可以实现线程安全。原因在于,在程序运行时就定义了对象,并对其初始化。之后,不管哪个线程调用成员函数getinstance(),都只不过是返回一个对象的指针而已。线程安全的,不需要在获取实例的成员函数中加锁.

//饿汉式:线程安全,注意一定要在合适的地方去delete它
class Singleton
{
public:
    static Singleton* getInstance();
private:
    Singleton(){}                                    //构造函数私有
    Singleton(const Singleton&) = delete;            //明确拒绝
    Singleton& operator=(const Singleton&) = delete; //明确拒绝
    static Singleton* m_pSingleton;
};

Singleton* Singleton::m_pSingleton = new Singleton();

Singleton* Singleton::getInstance()
{
    return m_pSingleton;
}

2. 工厂模式

作用:工厂模式主要解决接口选择的问题。该模式下定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,使其创建过程延迟到子类进行。
(工厂模式的主要作用是封装对象的创建,分离对象的创建和操作过程,用于批量管理对象的创建过程,便于程序的维护和扩展。工厂模式包括三种:简单工厂模式、工厂方法模式、抽象工厂模式。)

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。工厂模式作为一种创建模式,一般在创建复杂对象时,考虑使用;在创建简单对象时,建议直接new完成一个实例对象的创建。

2.1、简单工厂模式

主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类。使用简单工厂模式,我们只需要知道具体的产品型号就可以创建一个产品。

缺点:工厂类集中了所有产品类的创建逻辑,如果产品量较大,会使得工厂类变的非常臃肿。

//简单工厂模式
typedef enum ProductTypeTag
{
    TypeA,
    TypeB,
    TypeC
}PRODUCTTYPE;
class Product//产品抽象基类
{
public:
    virtual void Show() = 0;
};
class ProductA : public Product  //子类A
{
public:
    void Show()
    {
        cout<<"I'm ProductA"<<endl;
    }
};
class ProductB : public Product //子类B
{
public:
    void Show()
    {
        cout<<"I'm ProductB"<<endl;
    }
};
class ProductC : public Product //子类C
{
public:
    void Show()
    {
        cout<<"I'm ProductC"<<endl;
    }
};
class Factory//工厂类
{
public:
    Product* CreateProduct(PRODUCTTYPE type)
    {
        switch (type)
        {
            case TypeA:
                return new ProductA();
            case TypeB:
                return new ProductB();
            case TypeC:
                return new ProductC();
            default:
                return NULL;
        }
    }
};
int main()
{
    Factory productCreator;
    Product *productA=productCreator.CreateProduct(TypeA);
    Product *productB=productCreator.CreateProduct(TypeB);
    Product *productC=productCreator.CreateProduct(TypeC);
    productA->Show();
    productB->Show();
    productC->Show();
    if(productA){
        delete productA;
        productA=NULL;
    }
    if(productB){
        delete productB;
        productB=NULL;
    }
    if(productC){
        delete productC;
        productC=NULL;
    }
    return 0;
}

2.2、工厂方法模式

定义一个创建对象的接口,其子类去具体现实这个接口以完成具体的创建工作。如果需要增加新的产品类,只需要扩展一个相应的工厂类即可。

缺点:产品类数据较多时,需要实现大量的工厂类,这无疑增加了代码量。

//工厂方法模式
typedef enum ProductTypeTag
{
    TypeA,
    TypeB,
    TypeC
}PRODUCTTYPE;

class Product//产品抽象基类
{
public:
    virtual void Show() = 0;
};
class ProductA : public Product //具体的产品类A
{
public:
    void Show()
    {
        cout<<"I'm ProductA"<<endl;
    }
};
class ProductB : public Product //具体的产品类B
{
public:
    void Show()
    {
        cout<<"I'm ProductB"<<endl;
    }
};

//抽象工厂类,提供一个创建接口
class Factory 
{
public:
	//提供创建产品实例的接口,返回抽象产品类
    virtual Product *createProduct()=0;  
};

//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class FactoryA:public Factory 
{
public:
    Product *createProduct()
     {
        return new ProductA();
    }
};

//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class FactoryB:public Factory
 {
public:
    Product *createProduct()
     {
        return new ProductB();
    }
};

//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class FactoryC:public Factory
{
public:
    Product *createProduct(){
        return new ProductC();
    }
};
int main()
{
    Factory *factoryA=new FactoryA();  //将子类对象传给基类指针实现运行时多态
    Product *productA = factoryA->createProduct();
    productA->Show();
    Factory *factoryB=new FactoryB();
    Product *productB = factoryB->createProduct();
    productB->Show();
    if (factoryA) {
        delete factoryA;
        factoryA = NULL;
    }
    if (factoryB) {
        delete factoryB;
        factoryB = NULL;
    }
    if (productA) {
        delete productA;
        productA = NULL;
    }
    if (productB) {
        delete productB;
        productB = NULL;
    }
    return 0;
}

2.3、抽象工厂模式

  • 抽象工厂模式提供创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。当存在多个产品系列,而客户端只使用一个系列的产品时,可以考虑使用抽象工厂模式。

  • 抽象工厂模式对工厂方法模式进行了更加一般化的描述。工厂方法模式适用于产品种类结构单一的场合,为一类产品提供创建的接口;而抽象工厂方法适用于产品种类结构多的场合,就是当具有多个抽象产品类型时,抽象工厂便可以派上用场。

  • 抽象工厂模式更适合实际情况,受生产线所限,让低端工厂生产不同种类的低端产品,高端工厂生产不同种类的高端产品。

缺点:当增加一个新系列的产品时,不仅需要现实具体的产品类,还需要增加一个新的创建接口,扩展相对困难

/*
 * 关键代码:在一个工厂里聚合多个同类产品。
 */

//抽象工厂模式
class ProductA
{
public:
    virtual void Show() = 0;
};
class ProductA1 : public ProductA//A类低端产品
{
public:
    void Show()
    {
        cout<<"I'm ProductA1"<<endl;
    }
};
class ProductA2 : public ProductA//A类高端产品
{
public:
    void Show()
    {
        cout<<"I'm ProductA2"<<endl;
    }
};
class ProductB
{
public:
    virtual void Show() = 0;
};
class ProductB1 : public ProductB//B类低端产品
{
public:
    void Show()
    {
        cout<<"I'm ProductB1"<<endl;
    }
};
class ProductB2 : public ProductB//B类高端产品
{
public:
    void Show()
    {
        cout<<"I'm ProductB2"<<endl;
    }
};
class Factory  //工厂抽象类
{
public:
    virtual ProductA *CreateProductA() = 0;
    virtual ProductB *CreateProductB() = 0;
};
class Factory1 : public Factory//1号工厂用于生产低端产品
{
public:
    ProductA *CreateProductA()
    {
        return new ProductA1();
    }
    ProductB *CreateProductB()
    {
        return new ProductB1();
    }
};
class Factory2 : public Factory//2号工厂用于生产高端产品
{
    ProductA *CreateProductA()
    {
        return new ProductA2();
    }
    ProductB *CreateProductB()
    {
        return new ProductB2();
    }
};
int main()
{
    Factory *factory1 = new Factory1();
    ProductA *productA1 = factory1->CreateProductA();
    ProductB *productB1 = factory1->CreateProductB();
    productA1->Show();
    productB1->Show();
    Factory *factory2 = new Factory2();
    ProductA *productA2 = factory2->CreateProductA();
    ProductB *productB2 = factory2->CreateProductB();
    productA2->Show();
    productB2->Show();
    if (factory1)
    {
        delete factory1;
        factory1 = NULL;
    }
    if (productA1)
    {
        delete productA1;
        productA1= NULL;
    }
    if (productB1)
    {
        delete productB1;
        productB1 = NULL;
    }
    if (factory2)
    {
        delete factory2;
        factory2 = NULL;
    }
    if (productA2)
    {
        delete productA2;
        productA2 = NULL;
    }
    if (productB2)
    {
        delete productB2;
        productB2 = NULL;
    }
}

3.观察者模式

观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都要得到通知并自动更新。

观察者模式从根本上讲必须包含两个角色:观察者和被观察对象。

  • 被观察对象自身应该包含一个容器来存放观察者对象,当被观察者自身发生改变时通知容器内所有的观察者对象自动更新。

  • 观察者对象可以注册到被观察者的中,完成注册后可以检测被观察者的变化,接收被观察者的通知。当然观察者也可以被注销掉,停止对被观察者的监控。

/*
* 关键代码:在目标类中增加一个ArrayList来存放观察者们。
*/
#include <iostream>
#include <list>
#include <memory>
using namespace std;

class View;

//被观察者抽象类   数据模型
class DataModel
{
public:
    virtual ~DataModel(){}
    virtual void addView(View* view) = 0;
    virtual void removeView(View* view) = 0;
    virtual void notify() = 0;   //通知函数
};

//观察者抽象类   视图
class View
{
public:
    virtual ~View(){ cout << "~View()" << endl; }
    virtual void update() = 0;
    virtual void setViewName(const string& name) = 0;
    virtual const string& name() = 0;
};

//具体的被观察类, 整数模型
class IntDataModel:public DataModel
{
public:
    ~IntDataModel()
    {
        m_pViewList.clear();
    }
    
    virtual void addView(View* view) override
    {
        shared_ptr<View> temp(view);
        auto iter = find(m_pViewList.begin(), m_pViewList.end(), temp);
        if(iter == m_pViewList.end())
            m_pViewList.push_front(temp);
        else
            cout << "View already exists" << endl;
    }
    
    void removeView(View* view) override
    {
        auto iter = m_pViewList.begin();
        for(; iter != m_pViewList.end(); iter++) {
            if((*iter).get() == view) {
                m_pViewList.erase(iter);
                cout << "remove view" << endl;
                return;
            }
        }
    }
    
    virtual void notify() override
    {
        auto iter = m_pViewList.begin();
        for(; iter != m_pViewList.end(); iter++)
            (*iter).get()->update();
    }
    
private:
    list<shared_ptr<View>> m_pViewList;
};

//具体的观察者类    表视图
class TableView : public View
{
public:
    TableView() : m_name("unknow"){}
    TableView(const string& name) : m_name(name){}
    ~TableView(){ cout << "~TableView(): " << m_name.data() << endl; }
    
    void setViewName(const string& name)
    {
        m_name = name;
    }
    
    const string& name()
    {
        return m_name;
    }
    
    void update() override
    {
        cout << m_name.data() << " update" << endl;
    }
    
private:
    string m_name;
};

int main()
{
    /*
    * 这里需要补充说明的是在此示例代码中,View一旦被注册到DataModel类之后,DataModel解析时会自动解析掉     * 内部容器中存储的View对象,因此注册后的View对象不需要在手动去delete,再去delete View对象会出错。
    */

    View* v1 = new TableView("TableView1");
    View* v2 = new TableView("TableView2");
    View* v3 = new TableView("TableView3");
    View* v4 = new TableView("TableView4");
    
    IntDataModel* model = new IntDataModel;
    model->addView(v1);
    model->addView(v2);
    model->addView(v3);
    model->addView(v4);
    
    model->notify();
    
    cout << "-------------\n" << endl;
    
    model->removeView(v1);
    
    model->notify();
    
    delete model;
    model = nullptr;
    
    return 0;
}

4. 装饰器模式

装饰模式:动态地给一个对象添加一些额外的功能,它是通过创建一个包装对象,也就是装饰来包裹真实的对象。新增加功能来说,装饰器模式比生产子类更加灵活。

对已经存在的某些类进行装饰,以此来扩展一些功能,从而动态的为一个对象增加新的功能。装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。

以下情形考虑使用装饰模式:

  • 需要扩展一个类的功能,或给一个类添加附加职责。

  • 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。

  • 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。

  • 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

/*
* 关键代码:1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
*/
#include <iostream>
using namespace std;

//抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
class Component
{
public:
    virtual ~Component(){}
    virtual void configuration() = 0;
};

//具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
class Car : public Component
{
public:
    void configuration() override
    {
        cout << "A Car" << endl;
    }
};

//装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
class DecorateCar : public Component
{
public:
    DecorateCar(Component* car) : m_pCar(car){}
    void configuration() override
    {
        m_pCar->configuration();
    }
   
private:
    Component* m_pCar;
};

//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateLED : public DecorateCar
{
public:
    DecorateLED(Component* car) : DecorateCar(car){}
    void configuration() override
    {
        DecorateCar::configuration();
        addLED();
    }
   
private:
    void addLED()
    {
        cout << "Install LED" << endl;
    }
   
};

//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecoratePC : public DecorateCar
{
public:
    DecoratePC(Component* car) : DecorateCar(car){}
    void configuration() override
    {
        DecorateCar::configuration();
        addPC();
    }
   
private:
    void addPC()
    {
        cout << "Install PC" << endl;
    }
};

//具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
class DecorateEPB : public DecorateCar
{
public:
    DecorateEPB(Component* car) : DecorateCar(car){}
    void configuration() override
    {
        DecorateCar::configuration();
        addEPB();
    }
   
private:
    void addEPB()
    {
        cout << "Install Electrical Park Brake" << endl;
    }
};

int main()
{
    Car* car = new Car;
    DecorateLED* ledCar = new DecorateLED(car);
    DecoratePC* pcCar = new DecoratePC(ledCar);
    DecorateEPB* epbCar = new DecorateEPB(pcCar);
   
    epbCar->configuration();
    
    delete epbCar;
    epbCar = nullptr;
   
    delete pcCar;
    pcCar = nullptr;
   
    delete ledCar;
    ledCar = nullptr;
   
    delete car;
    car = nullptr;
   
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值