设计模式大概理解上

164 阅读5分钟

一、设计模式是什么?

1、计模式是指在软件开发中经过验证的,用于解决在特定的环境下,重复出现的,特定问题的解决方案
2、解决问题的固定套路 。 (注意慎用设计模式)

二、设计模式怎么来的?

1、满足设计原则后,慢慢迭代出来的。

三、设计模式解决了什么问题?

①:前提:既有变化点又有稳定点
②:少量的改变就能适应需求的变化。 比喻:整洁的房间,好动的猫。怎么保证房间的整洁?把猫关进一个笼子。

四、设计模式基础

1、面向对象思想

封装:隐藏实现细节,实现模块化
继承:无需修改原有类的情况下,实现对功能的扩展。
多态: ①:静态多态--->函数重载
②:动态多态,继承中的虚函数重写。

早绑定:在没有使用虚函数的时候发生。
晚绑定:使用虚函数的时候发生。
下面解释过程:

class Base{
public:
    virtual void func1(){}
    virtual void func2(){}
    int a;
};

image.png

class Subject:public Base{
public:
    virtual void func2(){}
    virtual void func3(){}
    int b;
};

image.png
由这2张图看出晚绑定就是Base*p = new Subject;时,Base会转换为Subject,p就变成了Subject类型。但是早绑定就不会转换,p还是Base类型。

2、设计原则

2.1、依赖倒置

  • 核心思想高层模块不应该依赖低层模块,二者都应该依赖抽象。抽象不应该依赖细节细节应该依赖抽象
  • 实现方式:通过接口或抽象类定义依赖关系,具体实现类依赖接口或抽象类。
class IAnimal {
public:
    virtual void speak() = 0;
};

class Dog : public IAnimal {
public:
    void speak() override {
        std::cout << "Woof!" << std::endl;
    }
};

class Cat : public IAnimal {
public:
    void speak() override {
        std::cout << "Meow!" << std::endl;
    }
};

void makeAnimalSpeak(IAnimal* animal) {
    animal->speak();
}

2.2、开发封闭

对扩展开发,对修改关闭

扩展方式 ①继承 ②多态组合

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

class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing Circle" << std::endl;
    }
};

class Square : public Shape {
public:
    void draw() override {
        std::cout << "Drawing Square" << std::endl;
    }
};

2.3、面向接口

  • 核心思想:通过接口定义行为,具体实现类实现接口。
  • 优点:提高代码的灵活性和可维护性。
class ILogger {
public:
    virtual void log(const std::string& message) = 0;
};

class FileLogger : public ILogger {
public:
    void log(const std::string& message) override {
        // Log to file
    }
};

class ConsoleLogger : public ILogger {
public:
    void log(const std::string& message) override {
        // Log to console
    }
};

2.4、封装变化点

  • 核心思想:将系统中可能变化的部分封装起来,减少对系统其他部分的影响。
  • 实现方式:使用protectedprivate关键字限制访问权限。
class PaymentProcessor {
protected:
    void validatePayment() {
        // Validation logic
    }
public:
    void processPayment() {
        validatePayment();
        // Process payment
    }
};

2.5、单一职责原则

  • 核心思想:一个类只负责一个职责,只有一个引起它变化的原因。
class Report {
public:
    void generateReport() {
        // Generate report
    }
};

class ReportPrinter {
public:
    void printReport(const Report& report) {
        // Print report
    }
};

2.6、里氏替换

  • 核心思想:子类可以替换父类,并且不会影响程序的正确性。子类继承父类的一些行为时,父类的行为要保存到子类去。
class Bird {
public:
    virtual void fly() {
        std::cout << "Flying" << std::endl;
    }
};

class Sparrow : public Bird {
public:
    void fly() override {
        std::cout << "Sparrow flying" << std::endl;
    }
};

2.7、接口隔离

  • 核心思想:客户端不应该依赖它不需要的接口。
  • 实现方式:将大接口拆分为小接口。
  1. 类封装,权限限定词来实现。private是只有自己可以用,public是用户、自己和子类都可以用。protected是给自己和子类使用。
  2. 类与类依赖 接口(依赖注入)

2.8、组合优于继承

  • 核心思想:优先使用组合而不是继承来实现代码复用。
class Engine {
public:
    void start() {
        std::cout << "Engine started" << std::endl;
    }
};

class Car {
private:
    Engine engine;
public:
    void start() {
        engine.start();
    }
};

2.9、最小知道原则

  • 核心思想:一个类应该只与直接相关的类交互,减少类之间的耦合。用户知道越少越好。
class Zooshow{
public:
    Zooshow(){}
public:
    void show(){
        if(show0()){
            Play();
        }
        show1();
    }
private://用户不需要知道他们怎么运行,所以可以直接封装起来
    bool show0(){}
    void Play(){}
    void show1(){}
};

五、设计模式如何学?

  1. 明确目的:在现有的设计模式基础上,扩展代码。
  2. 做功能抽象时,如何选择设计模式
  3. 通过看代码知道是哪个设计模式
  4. 符合的设计原则要清楚
  5. 如何扩展代码。
  6. 有哪些设计应用场景。

六、观察者模式

定义:定义对象间的一种一对多(变化)的依赖关系,以便当一个对象Subject的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

1、解决的问题

  • 稳定点:一对多的依赖关系,“一”变化“多”跟着变化

  • 变化点:①:“多”增加、②“多”减少

    为了让稳定点变的更稳定,需要将他们抽象。比如:

class control{
public:
    //调用ZooShow类。
    //如果ZooShow类 有多个对象使用
    //就需要抽象
    vector<Zooshow*>v;//如果不这样写
    ```
    //一变化则多也跟着变化,就需要一个函数来调用他们共同的函数了
    for(){
        v.show();
    }

    //就可能是写个函数封装起来
    void fun(){//每次增加对象都要填一个新的z,就不稳定了
        Zooshow* z1= new Zooshow;
        Zooshow* z2 = new Zooshow;
    }
};

稳定点用到了面向接口原则。变量类型抽象成了接口,而不是类。

七、策略模式

定义:定义一系列算法,把它们封装起来,并且使它们可互相替换,该模式使得算法可独立于使用它的客户程序而变化。

解决了什么问题
①:稳定点:客户程序与算法的调用关系
②:变化点:算法变化(新加算法和后期算法改变)

class VAC_GuoQing : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
class VAC_GuoQing2 : public VAC_GuoQing {
public:
    virtual double CalcPro(const Context &ctx){}
};

class VAC_Shengdan : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};

// 设计原则:接口隔离原则
// 组合、继承
// 组合基类指针
// 两种方法:1. 采用具体接口选择算法  2. 依赖注入
class Promotion {
public:
    Promotion(ProStategy *sss = nullptr) : s(sss){}
    ~Promotion(){}
    void Choose(ProStategy *sss) {
        // 条件选择
        if (sss != nullptr) {
            s = sss;
        }
    }
    double CalcPromotion(const Context &ctx){
        if (s != nullptr) {
            return s->CalcPro(ctx);
        }
        return 0.0L;
    }
private:
    ProStategy *s;
};