设计模式

设计模式 Design Patterns

学习资料

创建型模式

创建型模式提供了创建对象的机制, 能够提升已有代码的灵活性和可复用性。

单例模式 Singleton

Singleton is a creational design pattern that lets you ensure that
a class has only one instance,
while providing a global access point to this instance.

  1. 默认构造函数私有化
  2. 新建一个静态构建方法作为构造函数
/**
 * The Singleton class defines the `GetInstance` method that serves as an
 * alternative to constructor and lets clients access the same instance of this
 * class over and over.
 */
class Singleton
{

    /**
     * The Singleton's constructor/destructor should always be private to
     * prevent direct construction/desctruction calls with the `new`/`delete`
     * operator.
     */
private:
    static Singleton * pinstance_;
    static std::mutex mutex_;

protected:
    Singleton(const std::string value): value_(value)
    {
    }
    ~Singleton() {}
    std::string value_;

public:
    /**
     * Singletons should not be cloneable.
     */
    Singleton(Singleton &other) = delete;
    /**
     * Singletons should not be assignable.
     */
    void operator=(const Singleton &) = delete;
    /**
     * This is the static method that controls the access to the singleton
     * instance. On the first run, it creates a singleton object and places it
     * into the static field. On subsequent runs, it returns the client existing
     * object stored in the static field.
     */

    static Singleton *GetInstance(const std::string& value);
    /**
     * Finally, any singleton should define some business logic, which can be
     * executed on its instance.
     */
    void SomeBusinessLogic()
    {
        // ...
    }
    
    std::string value() const{
        return value_;
    } 
};

/**
 * Static methods should be defined outside the class.
 */

Singleton* Singleton::pinstance_{nullptr};
std::mutex Singleton::mutex_;

/**
 * The first time we call GetInstance we will lock the storage location
 *      and then we make sure again that the variable is null and then we
 *      set the value. RU:
 */
Singleton *Singleton::GetInstance(const std::string& value)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (pinstance_ == nullptr)
    {
        pinstance_ = new Singleton(value);
    }
    return pinstance_;
}

工厂模式

  1. 简单工厂模式:
    单工厂,生产各种产品
    弊端:
  1. 产品过多时导致工厂过于庞大,变成超级类
  2. 要生产新的产品就要向工厂添加分支
  1. 工厂方法模式:
    每个产品有单独的工厂
    优点:要生产新的产品添加新的工厂即可
  2. 抽象工厂模式:
    在工厂方法模式上提取出工厂接口,各个产品工厂都实现该工厂接口

生成器模式

适用场景:
· 相同的方法,不同的执行顺序,产生不同的结果。
· 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
· 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
· 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。

原型模式

重写拷贝函数

结构模式

把类或对象结合在一起形成一个更大的结构。一般是解决不同的类之间有不同关系的情况。

适配器模式

包含角色有目标接口,适配者类,适配器类
适配器类通过继承或引用适配者的对象,把适配者接口转换成目标接口

class Target {
 public:
  virtual ~Target() = default;

  virtual std::string Request() const {
    return "Target: The default target's behavior.";
  }
};

class Adaptee {
 public:
  std::string SpecificRequest() const {
    return ".eetpadA eht fo roivaheb laicepS";
  }
};

class Adapter : public Target {
 private:
  Adaptee *adaptee_;

 public:
  Adapter(Adaptee *adaptee) : adaptee_(adaptee) {}
  std::string Request() const override {
    std::string to_reverse = this->adaptee_->SpecificRequest();
    std::reverse(to_reverse.begin(), to_reverse.end());
    return "Adapter: (TRANSLATED) " + to_reverse;
  }
};

void ClientCode(const Target *target) {
  std::cout << target->Request();
}

桥接模式

例,形状+颜色可以组合成平方级数的类,更好的做法是将其中一个(如颜色)分离成接口(setcolor())。

组合模式

树形结构,管理员(非叶节点)与职员(叶节点)

装饰模式

在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
包含以下角色:
1.抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
2.具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
3.抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
4.具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
采用了装饰模式就只需要为每个装饰品生成一个装饰类即可,所以说就增加对象功能来说,装饰模式比生成子类实现更为灵活。

class Component {
 public:
  virtual ~Component() {}
  virtual std::string Operation() const = 0;
};

class ConcreteComponent : public Component {
 public:
  std::string Operation() const override {
    return "ConcreteComponent";
  }
};

class Decorator : public Component {
 protected:
  Component* component_;

 public:
  Decorator(Component* component) : component_(component) {
  }
  std::string Operation() const override {
    return this->component_->Operation();
  }
};

class ConcreteDecoratorA : public Decorator {
 public:
  ConcreteDecoratorA(Component* component) : Decorator(component) {
  }
  std::string Operation() const override {
    return "ConcreteDecoratorA(" + Decorator::Operation() + ")";
  }
};

class ConcreteDecoratorB : public Decorator {
 public:
  ConcreteDecoratorB(Component* component) : Decorator(component) {
  }

  std::string Operation() const override {
    return "ConcreteDecoratorB(" + Decorator::Operation() + ")";
  }
};

外观模式

外观模式最简单,它使得两种不同的类不用直接交互,而是通过一个中间件——也就是外观类——间接交互。外观类中只需要暴露简洁的接口,隐藏内部的细节,所以说白了就是封装的思想。

// 外观角色, 封装了三个子系统的方法
class Facade {
public:
    void method() {
        obj1->method1();
        obj2->method2();
        obj3->method3();
    }
private:
    SubSystem01 *obj1 = new SubSystem01();
    SubSystem02 *obj2 = new SubSystem02();
    SubSystem03 *obj3 = new SubSystem03();
};

享元模式

当系统中多处需要同一组信息时,可以把这些信息封装到一个对象中,然后对该对象进行缓存,这样,一个对象就可以提供给多出需要使用的地方,避免大量同一对象的多次创建,降低大量内存空间的消耗。
享元模式其实是工厂方法模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能。

代理模式

1.抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
2.真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
3.代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

行为模式

类和对象如何交互,及划分责任和算法。

观察者模式

  1. 拆分为两个部分: 独立于其他代码的核心功能将作为发布者; 其他代码则将转化为一组订阅类
  2. 声明订阅者接口。 该接口至少应声明一个 update方法。
  3. 声明发布者接口并定义一些接口来在列表中添加和删除订阅对象。 将列表放置在直接扩展自发布者接口的抽象类中是显而易见的。

迭代器模式

备忘录模式

模板方法模式

父类定义模板方法以及具体的各个方法;子类重写各个方法。

状态模式

类似状态机,为每个状态实现一个类

责任链模式

class Handler {
 public:
  virtual Handler *SetNext(Handler *handler) = 0;
  virtual std::string Handle(std::string request) = 0;
};

class AbstractHandler : public Handler {
private:
  Handler *next_handler_;

public:
  AbstractHandler() : next_handler_(nullptr) {
  }
  Handler *SetNext(Handler *handler) override {
    this->next_handler_ = handler;
    return handler;
  }
  std::string Handle(std::string request) override {
    if (this->next_handler_) {
      return this->next_handler_->Handle(request);
    }
    return {};
  }
};
/**
 * All Concrete Handlers either handle a request or pass it to the next handler
 * in the chain.
 */
class MonkeyHandler : public AbstractHandler {
 public:
  std::string Handle(std::string request) override {
    if (request == "Banana") {
      return "Monkey: I'll eat the " + request + ".
";
    } else {
      return AbstractHandler::Handle(request);
    }
  }
};

命令模式

不常用,暂忽略

中介者模式

中介者会逐渐变庞大,演变成上帝对象。

策略模式

策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。策略模式用来解耦策略的定义、创建、使用。实际上,一个完整的策略模式就是由这三个部分组成的。
最常见的应用场景是,利用它来避免冗长的if-else或switch分支判断。

模板方法模式

模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。

访问模式

访问者模式建议将新行为放入一个名为访问者的独立类中, 而不是试图将其整合到已有类中。
不常用。

设计模式总结

    1. 创建型模式
      单例模式、工厂模式频繁使用
    1. 结构型模式
      外观模式非常常用,间接封装,作用于整个对象子系统,可封装多个对象
      适配器模式能为被封装对象提供不同的接口,只封装一个对象
      代理模式能为对象提供相同的接口
      装饰模式则能为对象提供加强的接口,还支持递归组合
      享元模式展示了如何生成大量的小型对象
    1. 行为模式
      观察者模式支持订阅与发布
      迭代器模式支持集合的遍历
      备忘录模式即快照模式,支持恢复、防丢失
      状态模式实现了状态机,实现方式有 ifelse法、查表法、状态模式法,对于状态少业务逻辑多的场景首选状态模式
      责任链模式,继承包含了next_handler_变量的父类, 通过set_next_handler传导请求
      中介者模式,中介者会逐渐变庞大,演变成上帝对象。
      策略模式...
      模板方法模式...

设计模式准则

  • 开闭原则:一个软件实体如类、模块和函数应该对修改封闭,对扩展开放。
  • 单一职责原则:类只做一件事,一个类应该只有一个引起它修改的原因。
  • 里氏替换原则:子类应该可以完全替换父类。也就是说在使用继承时,只扩展新功能,而不要破坏父类原有的功能。
  • 依赖倒置原则:细节应该依赖于抽象,抽象不应依赖于细节。把抽象层放在程序设计的高层,并保持稳定,程序的细节变化由低层的实现层来完成。
  • 迪米特法则:又名「最少知道原则」,一个类不应知道自己操作的类的细节,换言之,只和朋友谈话,不和朋友的朋友谈话。
  • 接口隔离原则:客户端不应依赖它不需要的接口。如果一个接口在实现时,部分方法由于冗余被客户端空实现,则应该将接口拆分,让实现类只需依赖自己需要的接口方法。

    ref: https://www.cnblogs.com/linuxAndMcu/p/12408437.html
诸神对凡人心生艳羡,厌倦天堂。
原文地址:https://www.cnblogs.com/dirge/p/14656085.html