设计模式 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.
- 默认构造函数私有化
- 新建一个静态构建方法作为构造函数
/**
* 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_;
}
工厂模式
- 简单工厂模式:
单工厂,生产各种产品
弊端:
- 产品过多时导致工厂过于庞大,变成超级类
- 要生产新的产品就要向工厂添加分支
- 工厂方法模式:
每个产品有单独的工厂
优点:要生产新的产品添加新的工厂即可 - 抽象工厂模式:
在工厂方法模式上提取出工厂接口,各个产品工厂都实现该工厂接口
生成器模式
适用场景:
· 相同的方法,不同的执行顺序,产生不同的结果。
· 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
· 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
· 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。
原型模式
重写拷贝函数
结构模式
把类或对象结合在一起形成一个更大的结构。一般是解决不同的类之间有不同关系的情况。
适配器模式
包含角色有目标接口,适配者类,适配器类
适配器类通过继承或引用适配者的对象,把适配者接口转换成目标接口
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)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
行为模式
类和对象如何交互,及划分责任和算法。
观察者模式
- 拆分为两个部分: 独立于其他代码的核心功能将作为发布者; 其他代码则将转化为一组订阅类
- 声明订阅者接口。 该接口至少应声明一个 update方法。
- 声明发布者接口并定义一些接口来在列表中添加和删除订阅对象。 将列表放置在直接扩展自发布者接口的抽象类中是显而易见的。
迭代器模式
备忘录模式
模板方法模式
父类定义模板方法以及具体的各个方法;子类重写各个方法。
状态模式
类似状态机,为每个状态实现一个类
责任链模式
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分支判断。
模板方法模式
模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。
访问模式
访问者模式建议将新行为放入一个名为访问者的独立类中, 而不是试图将其整合到已有类中。
不常用。
设计模式总结
-
- 创建型模式
单例模式、工厂模式频繁使用
- 创建型模式
-
- 结构型模式
外观模式非常常用,间接封装,作用于整个对象子系统,可封装多个对象
适配器模式能为被封装对象提供不同的接口,只封装一个对象
代理模式能为对象提供相同的接口
装饰模式则能为对象提供加强的接口,还支持递归组合
享元模式展示了如何生成大量的小型对象
- 结构型模式
-
- 行为模式
观察者模式支持订阅与发布
迭代器模式支持集合的遍历
备忘录模式即快照模式,支持恢复、防丢失
状态模式实现了状态机,实现方式有 ifelse法、查表法、状态模式法,对于状态少业务逻辑多的场景首选状态模式
责任链模式,继承包含了next_handler_变量的父类, 通过set_next_handler传导请求
中介者模式,中介者会逐渐变庞大,演变成上帝对象。
策略模式...
模板方法模式...
- 行为模式
设计模式准则
- 开闭原则:一个软件实体如类、模块和函数应该对修改封闭,对扩展开放。
- 单一职责原则:类只做一件事,一个类应该只有一个引起它修改的原因。
- 里氏替换原则:子类应该可以完全替换父类。也就是说在使用继承时,只扩展新功能,而不要破坏父类原有的功能。
- 依赖倒置原则:细节应该依赖于抽象,抽象不应依赖于细节。把抽象层放在程序设计的高层,并保持稳定,程序的细节变化由低层的实现层来完成。
- 迪米特法则:又名「最少知道原则」,一个类不应知道自己操作的类的细节,换言之,只和朋友谈话,不和朋友的朋友谈话。
- 接口隔离原则:客户端不应依赖它不需要的接口。如果一个接口在实现时,部分方法由于冗余被客户端空实现,则应该将接口拆分,让实现类只需依赖自己需要的接口方法。
ref: https://www.cnblogs.com/linuxAndMcu/p/12408437.html