通用的、可复刻的人类解决复杂问题的方式:
分解:将一个复杂问题分解为多个简单的小问题,分而治之,逐个击破。
抽象:由于不能掌握全部的复杂对象,我们往往选择忽视非本质的问题而去处理泛化和理想化后的模型。
作为一个程序员应该有一个思维层次,就是不能静态的看待一个软件结构的设计,要有一个时间轴的概念,很多其他领域也是,如果你静态的来看问题,这个问题暴露不出来,但是时间长了就会出问题。

对象是什么:
从语言层面来说,对象封装了代码和数据;
从规格层面来说,对象是一系列可被调用的接口;
从概念上来说,对象是一种拥有责任的抽象。

设计模式的假设条件是必须含有稳定点和变化点,如果里面没有稳定的部分,那就没必要用设计模式了,同样如果全部都是稳定的,没有变化的部分也没必要使用设计模式了。
设计模式就是在稳定点和变化点之间寻找隔离点

1、设计原则

这里介绍了面向对象的八大设计原则,理解他比理解具体的设计模式更加重要

1.1 依赖倒置原则(DIP)

高层模块(稳定)不应该依赖低层模块(变化),二者都应该依赖于抽象(稳定)
抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象

1.2 开放封闭原则(OCP)

对扩展(继承父类)开放,对更改(更改父类)关闭
类模板应该是可扩展的,但是不能是可修改的。

1.3 单一职责原则(SRP)

一个类应该仅有一个引起它变化的原因
变化的方向隐含类的责任

1.4 Liskov替换原则(LSP)

子类必须能够替换他们的基类(is a)
继承可完全表达父类行为

1.5 接口隔离原则(ISP)

不应该强迫客户程序依赖到他们不用的方法
向外提供的接口应该小而完备

1.6 优先使用对象组合

类继承通常为“白箱复用”,对象组合为“黑箱复用”
继承在某种程度上破坏了封装性,子类和父类耦合度高

1.7 针对接口编程,而不是针对实现编程

不将变量类型声明为某个具体的类(line、rectangle),而是声明为某个接口(shape)

2、模板方法

2.1 模式分类:

1、从范围来分为 类模式和对象模式
类模式多指类与子类的静态关系,通常是子类继承父类
对象模式多指处理对象间的动态关系,类中包含另一个类的组合关系
2、从封装角度分
image.png
寻求设计需求中稳定部分和变化部分,寻找变化的点

2.2 重构关键技巧

C  设计模式 - 图2

3、模板模式

在最早的应用程序设计架构中,都是以早绑定的方式实现,application人员调用library库函数来实现
而模板模式是在library里实现了具体流程,application人员只是重写某些步骤的子类方法,“你不要调用我,我来调用你”实现晚绑定。
定义:定义一个算法骨架(稳定),而将一些步骤延迟到子类实现,使得子类可以通过重写虚函数来重定义该算法的某些特定步骤。
举例:我们做菜可以分为三个步骤,1、备料;2、具体做菜;3、盛菜给具体的客人享用
这三个流程就是骨架(稳定),然而具体的实现步骤可以不同,买不同的食材,具体做菜的方法也可以改变,给不同的客人吃都是可以改变的。

优点: (1) 具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。 (2) 代码复用的基本技术,在数据库设计中尤为重要。 (3) 存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合”开闭原则” 缺点: 每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。

4、策略模式

将各个上下文流程(具体的算法实现)抽象出来,继承自策略对象的父类。
各个上下文共享同一个strategy对象
定义:定义一系列的算法,把他们封装起来(基类),使得他们可互相替换(子类继承),且算法之间变化不会影响使用算法的客户。

  1. class TaxStrategy{
  2. public:
  3. virtual double Calculate(const Context& context) = 0;
  4. virtual ~TaxStrategy(){}
  5. };
  6. class SalesOrder{
  7. private:
  8. TaxStrategy*strategy;
  9. public:
  10. Sales0rder(StrategyFactory* strategyFactory){
  11. this->strategy = strategyFactory->NewStrategy();
  12. }
  13. ~Salesorder(){
  14. delete this->strategy;
  15. }
  16. public:
  17. double CalculateTax(){
  18. //......
  19. Context context();
  20. double val = strategy->Calculate(context);l l ...
  21. }
  22. };

优点: 1、算法可以自由切换。2、避免使用多重条件判断。3、扩展性良好。 缺点: 1、策略类会增多。2、所有策略类都需要对外暴露。

5、观察者模式

定义:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象,在它的状态发生变化时,会通知所有的观察者. 腾讯会议的多窗口?

6、装饰者模式

定义:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性。
对于父类和子类是组合关系
举例:

10、单例模式

有些特殊的类,在系统中只能存在一个实例,才能保证他的逻辑正确性。
所以这里的责任应该是类的设计者所考虑到的,而不应该将责任推给类的使用者

  1. class Singleton{
  2. private: //这里必须要设置为私有属性,默认的都是public
  3. Singleton();
  4. Singleton(const Singleton& other);
  5. public:
  6. static Singleton* getInstance(); //静态成员函数用于返回静态成员变量
  7. static Singleton* m_instance; //静态成员变量(因为创建的单例是堆对象)
  8. }
  9. Singleton* Singleton::m_instance = nullptr;
  10. //单线程的环境
  11. Singleton* Singleton::getInstance() {
  12. if (this->m_instance == nullptr){
  13. m_instance = new Singleton(); //多线程可能都会执行这行语句(加锁)
  14. }
  15. return this->m_instance;
  16. }
  17. //多线程的环境,但这个版本的锁代价太高,对于m_instance若不是空,仅仅是读操作就没必要加锁了
  18. Singleton* Singleton::getInstance() {
  19. Lock lock; //对于多线程加锁
  20. if(m_instance == nullptr) {
  21. m_instance = new Singleton();
  22. }
  23. return m_instance;
  24. }
  25. //2、多线程环境,双检查锁
  26. Singleton* Singleton::getInstance() {
  27. if(m_instance==nullptr) {
  28. Lock lock;
  29. if(m_instance == nullptr) {
  30. m_instance = new Singleton();
  31. }
  32. }
  33. return m_instance;
  34. }