设计模式--Note--大纲

Author:wangml

Date:20210904

参考课程:C++设计模式_哔哩哔哩_bilibili

在面对重复出现的问题,使用一套可复用的解决方案,避免重复劳动。

目标

  1. 理解松耦合设计思想
  2. 掌握面向对象设计原则
  3. 掌握重构技法改善设计
  4. 掌握GOF核心设计模式

分解与抽象

以实现一个画图窗口为例

// 分解 分而治之的思想
class Window {
public:
    void drawWindow() {
        ...
        for (auto line : lines) {
            line.drawLine();
        }
        
        for (auto circle : circles) {
            circle.drawCircle();
        }
        
        // 增加
        ...
    }
private:
    vector<Line> lines;
    vector<Circle> circles;
    // 增加
};

class Line {
public:
	Line();
    void drawLine();
private:
    Point a;
    Point b;
};

class Circle {
public:
    Circle();
    void drawCircle();
private:
    Point o;
    double len;
};

...
// 抽象
class Window {
public:
    void drawWindow() {
        ...
        for (auto shape : shapes) {
            shape->draw();
        }
        ...
    }
private:
    vector<Shape*> shapes;// 析构时 注意销毁
};

class Shape {
public:
    Shape();
    virtual void draw() {}
}

class Line : public Shape {
public:
	Line();
    virtual ~Line();// 使得通过Shape指针可以调用正确的析构函数
    void draw();
private:
    Point a;
    Point b;
};

class Circle : public Shape {
public:
    Circle();
    virtual ~Circle();
    void draw();
private:
    Point o;
    double len;
};

考虑,当系统需要增加一个三角形的类

此时除了实现一个新的类之外,抽象相对于分解的方法,对原代码的的改动要少得多

// 分解
class Triangle {
public:
    ...
    void drawTriangle();
};


// 抽象
class Triangle : public Shape {
public:
    void draw();
    ...
};

面向对象的设计

变化是复用的天敌

面向对象与软件设计

  1. 隔离变化:将变化带来的影响降为最小

  2. 微观层面:各司其职,新产生的类不影响已存在的类

    类的责任

对象

  1. 语言层面:封装了数据和代码
  2. 规格层面:一系列可被使用的公共接口
  3. 概念层面:拥有某种责任的抽象

面向对象设计原则

依赖倒置原则DIP

  1. 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)。

  2. 抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。

    这两换句话在上述Window的例子中的体现:

    1. Window不应该因为各个形状(Line等)的变化,而需要调整自身,Window和Line等都转而依赖于抽象的Shape
    2. 在Shape中不应该使用具体的(Line等)的操作,而是让Line等自己负责自身的实现细节(draw)

开放封闭原则OCP

  1. 对扩展开放,对更改封闭

  2. 类模块应该是可扩展的,但不可修改

    在Window中,可以扩展更多的形状(Triangle),而不比对Window做出更改

单一职责原则SRP

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

Liskov替换原则LSP

  1. 子类必须能够替换他们的基类(IS-A)
  2. 继承表达类型抽象

接口隔离原则ISP

  1. 不应该强迫客户程序依赖它们不用的方法

  2. 接口应该小而完备

    只public必要的,仅子类用的就protected,仅自身使用就private

优先使用对象组合而不是类继承

  1. 类继承通常为白箱复用,对象组合通常为黑箱复用

  2. 继承在某种程度上破坏了封装性,子类父类耦合度高

    继承有时候导致父类对子类暴露的东西过多

  3. 而对象组合只要求被组合的对象具有良好的接口,耦合程度低

封装变化点

  1. 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良影响,从而实现层次之间的松耦合

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

  1. 不将变量类声明为某个特定的具体类,而是声明为某个接口

  2. 客户程序无需获知对象的具体类型,只需知道对象所具有的接口

  3. 减少系统中各部分的依赖关系,从而实现 高内聚,松耦合 的类型设计方案

    Window的实现中,分解的实现包含了具体类的vector,而抽象实现使用Shape*

GOF-23

模式分类

  1. 从目的来看
    1. 创建型模式
    2. i结构型模式
    3. 行为型模式
  2. 从范围看
    1. 类模式处理类与子类的静态关系
    2. 对象模式处理对象间的动态关系

从封装变化角度对模式分类

  1. 组件协作

    框架与应用软件之间的划分

    组件协作模式通过晚期绑定来实现框架和应用程序之间的松耦合

    1. Template Method
    2. Strategy
    3. Observer / Event
  2. 单一职责

    1. Decorator
    2. Bridge
  3. 对象创建

    通过”对象创建”模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。

    它是接口抽象之后的第一步工作。

    1. Factory Method
    2. Abstract Factory
    3. Prototype
    4. Builder
  4. 对象性能

    1. Singleton
    2. Flyweight
  5. 接口隔离

    1. Facade
    2. Proxy
    3. Mediator
    4. Adapter
  6. 状态变化

    1. Memento
    2. State
  7. 数据结构

    1. Composite
    2. Iterator
    3. Chain of Resposibility
  8. 行为变化

    1. Command
    2. Visitor
  9. 领域问题

    1. Interpreter

重构关键技法

  1. 静态到动态

  2. 早绑定到晚绑定

  3. 继承到组合

  4. 编译时依赖到运行时依赖

  5. 紧耦合到松耦合

    从不同角度阐述同一个问题?

posted @ 2021-09-08 17:08  荒唐了年少  阅读(134)  评论(0编辑  收藏  举报