设计模式

一、概述

(一)、基本概念

  1. 底层思维(向下)

    • 语言构造
    • 编译转换
    • 内存模型
    • 运行时机制
  2. 抽象思维(向上)

    • 面向对象
    • 组件封装
    • 设计模式(依赖封装,继承,多态)
    • 架构模式
  3. 设计模式的使用依赖于某个稳定点。在设计中,要关注变化点和稳定点

  4. 目标:管理变化,提高复用

  5. 手段

    • 分解:先看个性
    • 抽象:再找共性
  6. 重构技法

    • 静态 => 动态(类 => 接口)
    • 早绑定 => 晚绑定(虚函数)
    • 继承 => 组合
    • 编译时依赖 => 运行时依赖
    • 紧耦合 => 松耦合
  7. 常用对象模式

    class A{
        B* pb;  //松耦合,具有灵活性
        //...
    };
    
  8. 不用设计模式的情况

    • 代码可读性很差
    • 需求理解很浅(避免花大功夫,做低效率的事)
    • 变化没有显现时
    • 不是系统的关键已来点
    • 项目没有复用价值时
    • 项目将要发布时
  9. 经验

    • 要有Framework和Application的区隔思维
      • 作为Application的开发人员,要认清Framework的扩展点
      • 作为Framework的开发人员,要设计Framework的接口
    • 良好的设计师演化的结果

(二)、面向对象设计原则(八)

最高层次:忘掉模式,只有原则

依赖导致原则(DIP)

  1. 高层模块(稳定)不应该底层模块(变化),二应该依赖于抽象(稳定)
    • 改进前:
      • People直接抚摸cat
      • cat发出miao回应
    • 改进后:
      • people抚摸宠物抽象类
      • cat继承抽象类中的回应虚函数发出回应
      • 即people和cat之间没有直接关系
  2. 抽象(稳定)不应该依赖于实现细节(变化),细节应该依赖于抽象(稳定)
  3. 举例:由Tomcat框架调用应用,而不是反过来应用调用框架(这在面向对象的设计中是极为常见的)

开放封闭原则(OCP)

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

单一职责原则(SRP)

  1. 一个类只负责一件事
  2. 一个类只有一个引起它变化的原因

Liskov替换原则(LSP)

  1. 子类必须能够替换他们的基类(IS-A)

接口隔离原则(ISP)

  1. 不应该强迫客户程序依赖它们不用的方法
  2. 接口应该小而完备

对象组合优于类继承

  1. 继承耦合度高,组合耦合度低

封装变化点

  1. 封装的作用是是一侧稳定,一侧变化

面向接口编程

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

(三)、将设计原则提升为设计经验

  1. 设计习语:和低层语言相关
  2. 设计模式:代码复用
  3. 架构模式:子系统划分等

(四)、重构

敏捷软件开发:Refactoring to Patterns (迭代的方法)

  1. 常见的重构技法
    • 静态 => 动态
    • 早绑定 => 晚绑定(虚函数)
    • 继承 => 组合
    • 编译时依赖 => 运行时依赖
    • 紧耦合 => 松耦合

(五)、常见分类

  1. 创建型:描述“怎样创建对象”
  2. 结构型:描述如何将类或对象按某种布局组成更大的结构
  3. 行为型:描述类或对象之间怎样相互协作共同玩=完成某个对象都无法单独完成的任务

二、设计模式

image-20220316102041800

(一)、组件协作模式

框架-组件

模板方法(Template Method)

  1. 背景

    • 在一段内置代码
    • 有一部分是变化的
    • 有一部分是不变化的
  2. code

    • 修改前

      • main,cpp

        #include <iostream>
        
        int main() {
            //step1(...);  不变,可以看做是一代代码而非函数
            //step2(...);    变化,可以看做是一代代码而非函数
            //step3(...);  不变,可以看做是一代代码而非函数
            //step4(...);    变化,可以看做是一代代码而非函数
            //step5(...);  不变,可以看做是一代代码而非函数
            return 0;
        }
        
    • 修改后

      • main.cpp

        #include <iostream>
        
        class BaseEvent{
        public:
            void run(){  //关键
                //step1(...);  不变,可以看做是一代代码而非函数
                step2();  //变化
                //step3(...);  不变,可以看做是一代代码而非函数
                step4();  //变化
                //step5(...);  不变,可以看做是一代代码而非函数
            }
            
            ~BaseEvent(){}
        
        protected:  //由于step2,step4仅供函数内部调用,故可以写作protected
            virtual void step2()=0;
            virtual void step4()=0;
        };
        
        class Event:public BaseEvent{
            virtual void setp2(){
                
            }
            
            virtual void step4(){
                
            }
        };
        
        int main() {
            BaseEvent *e = new Event();
            e->run();
            return 0;
        }
        
  3. 类图

    image-20220316121041412
  4. 定义:定义一个基类对象,通过run函数执行代码。对于代码中变化的部分,写作虚函数的调用。子类继承基类,实现对应的变化部分的虚函数,通常由上层调用run方法

  5. 注:

    • 在面向过程的语言中,通常由我们来调用lab中的函数;而在面向对象的思维中,通常会使用的一些框架(FrameWork),通常这个时候就变成了框架调用我们缩写的函数
    • 该模式在框架中使用较多
  6. 典型应用:Java的HttpServletservice方法调用doGet,doPost等方法,多线程中的run()方法

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String method = req.getMethod();
            long lastModified;
            if (method.equals("GET")) {
                lastModified = this.getLastModified(req);
                if (lastModified == -1L) {
                    this.doGet(req, resp);
                } else {
                    long ifModifiedSince;
                    try {
                        ifModifiedSince = req.getDateHeader("If-Modified-Since");
                    } catch (IllegalArgumentException var9) {
                        ifModifiedSince = -1L;
                    }
    
                    if (ifModifiedSince < lastModified / 1000L * 1000L) {
                        this.maybeSetLastModified(resp, lastModified);
                        this.doGet(req, resp);
                    } else {
                        resp.setStatus(304);
                    }
                }
            } else if (method.equals("HEAD")) {
                lastModified = this.getLastModified(req);
                this.maybeSetLastModified(resp, lastModified);
                this.doHead(req, resp);
            } else if (method.equals("POST")) {
                this.doPost(req, resp);
            } else if (method.equals("PUT")) {
                this.doPut(req, resp);
            } else if (method.equals("DELETE")) {
                this.doDelete(req, resp);
            } else if (method.equals("OPTIONS")) {
                this.doOptions(req, resp);
            } else if (method.equals("TRACE")) {
                this.doTrace(req, resp);
            } else {
                String errMsg = lStrings.getString("http.method_not_implemented");
                Object[] errArgs = new Object[]{method};
                errMsg = MessageFormat.format(errMsg, errArgs);
                resp.sendError(501, errMsg);
            }
    
        }
    

策略模式(Strategy Method)

  1. 背景

    • 我们有许多宠物
    • 每个宠物发出不同的叫声
  2. code

    • 修改前

      • main.cpp

        #include <iostream>
        
        using namespace std;
        
        enum Pet{
            dog,cat
        }pet;
        
        int main() {
            int petName;
            if(petName == dog){
                cout << "wang wang" <<endl;
            }else if(petName == cat){
                cout << "miao miao" << endl;
            }
            return 0;
        }
        
      • 为了体现设计模式的优越性,显然我们应该以动态的视角去考察,假设我们现在要增加一个动物chicken

      • main.cpp

        #include <iostream>
        
        using namespace std;
        
        enum Pet{
            dog,cat,
            chicken  //修改
        }pet;
        
        int main() {
            int petName;
            if(petName == dog){
                cout << "wang wang" <<endl;
            }else if(petName == cat){
                cout << "miao miao" << endl;
            }else if(petName == chicken){  //修改
                cout << "ge ge" << endl;
            }
            return 0;
        }
        
      • 显然这不算是一段好的代码,因为它违反了开闭原则

    • 修改后

      • main.cpp

        #include <iostream>
        #include "Person.h"
        
        using namespace std;
        
        int main() {
            PetFactory *petFactory = new ChickenFactory();
            Person p(petFactory);
            p.touchAnimal();
            return 0;
        }
        
      • Person.h

        //
        // Created by Arno_vc on 2022/3/15.
        //
        
        #ifndef DESIGNMODE_PERSON_H
        #define DESIGNMODE_PERSON_H
        
        #include "PetFactory.h"
        
        class Person {
        public:
            PetFactory *petFactory;
            Person(PetFactory * petFactory){
                this->petFactory = petFactory;
            }
        
            void touchAnimal(){
                Pro *pet = petFactory->buildPro();
                pet->howl();
            }
            
            ~Person(){}
        };
        
        
        #endif //DESIGNMODE_PERSON_H
        
      • PetFactory.h

        //
        // Created by Arno_vc on 2022/3/15.
        //
        
        #ifndef DESIGNMODE_PETFACTORY_H
        #define DESIGNMODE_PETFACTORY_H
        
        #include "Pro.h"
        
        class PetFactory {
        public:
            virtual Pro* buildPro()=0;
            ~PetFactory(){}
        };
        
        class DogFactory:public PetFactory{
        public:
            Pro* buildPro() override{
                return new DogPro();
            }
        };
        
        class CatFactory:public PetFactory{
        public:
            Pro* buildPro() override{
                return new CatPro();
            }
        };
        
        class ChickenFactory:public PetFactory{  //增加了一个新的工厂
        public:
            Pro* buildPro() override{
                return new ChickenPro();
            }
        };
        
        
        #endif //DESIGNMODE_PETFACTORY_H
        
      • Pro.h

        //
        // Created by Arno_vc on 2022/3/15.
        //
        
        #ifndef DESIGNMODE_PRO_H
        #define DESIGNMODE_PRO_H
        
        #include <string>
        #include <iostream>
        
        using namespace std;
        
        class Pro {
        protected:
            string name;
        public:
            virtual void howl()=0;
        };
        
        class CatPro:public Pro{
        public:
            void howl() override{
                cout << "miao miao miao" << endl;
            }
        };
        
        class DogPro:public Pro{
        public:
            void howl() override{
                cout << "wang wang wang" << endl;
            }
        };
        
        class ChickenPro:public Pro{  //增加了一个新的类
        public:
            void howl() override{
                cout << "ge ge ge" << endl;
            }
        };
        
        
        #endif //DESIGNMODE_PRO_H
        
      • 注:

        • 可以看到当出现新的宠物,需要增加两个类而不是修改
        • 注意与工厂模式的区别:
          • 策略模式强调子类之间平行的替换
          • 工厂模式强调调用的无惯性
  3. 定义:定义一系列算法,把它们一个个封装起来,使它们可以相互替换。具体方法为子类化扩展

  4. 类图

    image-20220316134600487
  5. 注:

    • 搭配工厂模式使用
    • 通常用于对if-else语句的优化
    • if-else是不稳定的,可以使用策略模式(如拥有的宠物);当if-else是稳定的,不能使用策略模式(如一星期有七天)
    • 性能影响:
      • 若使用if-else,编译后必然是整块代码
      • 若使用策略模式,链接器只链接所用到的相关类的部分代码
  6. 应用:Java的SPI机制

观察者模式(Observer)

  1. 背景

    • 水果店降价
    • wechat平台观察到水果店降价而发出通知(现实中当然存在水果店通知微信的关系,但我们这里仅以此为例)
  2. code

    • 修改前

      • main.cpp

        #include <iostream>
        #include <vector>
        
        using namespace std;
        
        class Wechat{
        public:
            void NotifyAccount(int num){
                cout << "Now we have " << num << " account" << endl;
            }
        };
        
        class Shop{
        public:
            int price;
            Wechat *we;
            Shop(Wechat *we):we(we){};
            void decreasePrice(int num){
                price-=num;
                we->NotifyAccount(num);
            }
        };
        
        int main() {
            Wechat *we = new Wechat();
            Shop s(we);
            we->NotifyAccount(10);
            return 0;
        }
        
      • 注:显然我们这里对vip做的是一个通知。可以看到这里违背了依赖倒置的关系,一方面微信的通知是依赖于价格的变化的,另一方面却是由商店主动通知微信

    • 修改后

      • main.cpp

        #include <iostream>
        #include <vector>
        
        using namespace std;
        
        class Platform{  //用于解耦的Observer
        public:
            virtual void NotifyAccount(int num)=0;
        };
        
        class Wechat: public Platform{
        public:
            virtual void NotifyAccount(int num){
                cout << "Wechat:Now we have " << num << " account" << endl;
            }
        };
        
        class Alipay: public Platform{
        public:
            virtual void NotifyAccount(int num){
                cout << "Alipay:Now we have " << num << " account" << endl;
            }
        };
        
        class Shop{
        public:
            int price;
            vector<Platform *> platforms;  //通过vector以支持多个通知对象
            void decreasePrice(int num){
                price-=num;
                vector<Platform *>::iterator it=platforms.begin();
                while(it!=platforms.end()){
                    (*it)->NotifyAccount(num);
                    it++;
                }
            }
        };
        
        int main() {
            Platform *we = new Wechat();
            Platform *allipay = new Alipay();
            Shop shop;
            shop.platforms.push_back(we);
            shop.platforms.push_back(allipay);
            shop.decreasePrice(10);
            return 0;
        }
        
      • 注:同时支持了多平台

  3. 类图

    image-20220316162312133
  4. 定义:对象之间的一对多关系,当某一事物状态发生改变时,所有依赖于它的对象都能得到通知(通过Observer对象)并自动更新

  5. 注:

    • Observer模式是基于事件的UI框架中非常常用的设计模式,也就是MVC模式的一个重要组成部分
    • 实际上这里Object也可以分为抽象的Object(包含notify,以及对vector的操作)和Concrete Object,因为就代码中而言这个水果店开业一般化为商店。但就结果而言,在观察者模式下商店与特定的平台已经无关了

(二)、单一职责

划清责任,避免子类膨胀

装饰者模式与桥模式的区别:

  • 装饰者模式:装饰部分可以组合
  • 桥模式:装饰部分不组合

装饰模式(Decorate Mode)

  1. 背景

    • 吹水果
    • 有三种水果,苹果,香蕉,葡萄
    • 有三种不同的吃法,直接吃,洗完吃,削皮吃
  2. code

    • 修改前

      • main.cpp

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Fruit{
            virtual void eatFruit()=0;
            virtual ~Fruit();
        };
        
        class Apple:public Fruit{
            virtual void eatFruit(){
                cout << "Apple : Eat directly!" << endl;
            }
            virtual ~Apple();
        }
        
        class Banana: public Fruit{
             virtual void eatFruit(){
                cout << "Banana : Eat directly!" << endl;
            }
            virtual ~Banana();
        };
        
        class Grape: public Fruit{
             virtual void eatFruit(){
                cout << "Grape : Eat directly!" << endl;
            }
            virtual ~Grape();
        };
        
        class WashApple: public Fruit{
            virtual void eatFruit(){
                cout << "wash" << endl;
                cout << "Apple : Eat directly!" << endl;
            }
            virtual ~WashApple();
        };
        
        class WashBanana: public Fruit{
            virtual void eatFruit(){
                cout << "wash" << endl;
                cout << "Banana : Eat directly!" << endl;
            }
            virtual ~WashBanana();
        };
        
        class WashGrape: public Fruit{
            virtual void eatFruit(){
                cout << "wash" << endl;
                cout << "Grape : Eat directly!" << endl;
            }
            virtual ~WashGrape();
        };
        
        class PeelApple: public Fruit{
            virtual void eatFruit(){
                cout << "Peel" << endl;
                cout << "Apple : Eat directly!" << endl;
            }
            virtual ~PeelApple();
        };
        
        class PeelBanana: public Fruit{
            virtual void eatFruit(){
                cout << "Peel" << endl;
                cout << "Banana : Eat directly!" << endl;
            }
            virtual ~PeelBanana();
        };
        
        class PeelGrape: public Fruit{
            virtual void eatFruit(){
                cout << "Peel" << endl;
                cout << "Grape : Eat directly!" << endl;
            }
            virtual ~PeelGrape();
        };
        
        class WashAndPeelApple{
            //...
        }
        
        class WashAndPeelBanana{
            //...
        }
        
        class WashAndPeelGrape{
            //...
        }
        
      • 存在问题:当有n个子类,m种修饰(在本例中为洗和剥,实际上它们是可以相互组合的)的情况下,我们对于同一个动作吃可以派生出$$1+n+n\frac{(m+1)m}{2}$$(在李建忠视频中给出的是$$1+n+n*m!/2$$)种类,这是非常恐怖。观察代码,我们可以发现:

        • wash和peel实际上都是对eat的附加操作,借此我们可以进行优化
        • 先wash,后peel
      • 同时将几种修饰结合,违背了单一职责原则

    • 修改后

      • main.cpp

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Fruit{
            virtual void eatFruit()=0;
            virtual ~Fruit();
        };
        
        class Apple: public Fruit{
            virtual void eatFruit(){
                cout << "Apple : Eat directly!" << endl;
            }
            virtual ~Apple();
        };
        
        class Banana: public Fruit{
            virtual void eatFruit(){
                cout << "Banana : Peel to eat!" << endl;
            }
            virtual ~Banana();
        };
        
        class Grape: public Fruit{
            virtual void eatFruit(){
                cout << "Eat : Peel to eat!" << endl;
            }
            virtual ~Grape();
        };
        
        class DecorateGrape:public Fruit{  //修饰类
            Fruit *fruit;
            DecorateGrape(Fruit *f):fruit(f){}
            virtual void eatFruit(){}=0;
            virtual ~DecorateGrape();
        };
        
        class WashFruit:public DecorateGrape{  //修饰类
            WashFruit(Fruit *f):fruit(f){}
            virtual void eatFruit(){
                cout << "Wash" << endl;
                fruit->eatFruit();
            }
            virtual ~WashFruit();
        };
        
        class PeelBanana:public DecorateGrape{  //修饰类
            PeelBanana(Fruit *f):fruit(f){}
            virtual void eatFruit(){
                cout << "Peel" << endl;
                fruit->eatFruit();
            }
            virtual ~PeelBanana();
        };
        
        int main(){
            Fruut a = new Apple();
            Fruit pa = new PeelFruit(a);
            Fruit wa = new WashFruit(pa);
            pa.eat();  //自下而上:wash => peel => eat,简直妙极了
            return 0;
        }
        
      • 评价

        • 通过继承规范了接口
        • 通过组合实现了接口
        • 通过修饰与修饰间的先后关系可以方便地进行组合
        • 通过修饰模式所构建的类个数为$$1+n+1+m$$
  3. 类图

    image-20220316205633952
  4. 定义:动态(通过组合方式)地给一个对象增加一些额外的职责,这些职责可以方便地组合在一起

  5. 注:

    • 注意对于修饰的理解。在本例中,苹果,香蕉的是对于水果的的扩充,但洗和剥确实另一个方向的(或者说是修饰)
    • 继承(重写接口)+组合
    • Decorator类在接口上表现为继承关系(用于完善接口规范),在实现上表现为组合关系(用来支持实现)

桥模式(Bridge Method)

  1. 背景(还有一个更简单的背景理解,就是一杯咖啡,存在两个大小和添加的小料——糖等——两个维度,以大小作为主维度去思考)

    • 在平台上购物
    • 平台:水果,零食,衣服
    • 会员与非会员
  2. code

    • 修改前

      • main.cpp

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Buyer{
        public:
            virtual void order()=0;
        };
        
        class ClothesBuyer: public Buyer{
        public:
            virtual void order(){
                cout << "order" << endl;
                cout << "buy clothes" << endl;
            }
        };
        
        class FruitBuyer: public Buyer{
        public:
            virtual void order(){
                cout << "order" << endl;
                cout << "buy Fruit" << endl;
            }
        };
        
        class FoodBuyer: public Buyer{
        public:
            virtual void order(){
                cout << "order" << endl;
                cout << "buy Food" << endl;
            }
        };
        
        class ClothesBuyerLite: public Buyer{  //不同平台的会员与非会员的业务
        public:
            virtual void order(){
                cout << "lite account" << endl;
                cout << "buy clothes" << endl;
            }
        };
        
        class ClothesBuyerPerfect: public Buyer{  //不同平台的会员与非会员的业务
        public:
            virtual void order(){
                cout << "perfect account" << endl;
                cout << "buy clothes" << endl;
            }
        };
        
        class FruitBuyerLite: public Buyer{  //不同平台的会员与非会员的业务
        public:
            virtual void order(){
                cout << "lite account" << endl;
                cout << "buy Fruit" << endl;
            }
        };
        
        class FruitBuyerPerfect: public Buyer{  //不同平台的会员与非会员的业务
        public:
            virtual void order(){
                cout << "perfect account" << endl;
                cout << "buy Fruit" << endl;
            }
        };
        
        class FoodBuyerLite: public Buyer{  //不同平台的会员与非会员的业务
        public:
            virtual void order(){
                cout << "lite account" << endl;
                cout << "buy Food" << endl;
            }
        };
        
        class FoodBuyerPerfect: public Buyer{  //不同平台的会员与非会员的业务
        public:
            virtual void order(){
                cout << "perfect account" << endl;
                cout << "buy Food" << endl;
            }
        };
        
        int main() {
            FoodBuyerLite foodBuyerLite;
            foodBuyerLite.order();
            return 0;
        }
        
      • 存在问题:

        • 在order中我们可以发现,存在两个不同的维度,即是否为会员,具体的平台。该做法违反了单一职责原则
        • 将维度展开,n个平台,m个修饰(这里的修饰是不能进行组合的),需要的类个数为$$1+n+n*m$$
        • 修改目标:减少组合
    • 修改后

      • main.cpp

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class BuyerImpl{
        public:
            virtual void buy()=0;
        };
        
        //平台实现(与Buyer无关,只包含了紧密相关的业务)
        class ClothesBuyer: public BuyerImpl{
        public:
        
            virtual void buy(){
                cout << "buy clothes" << endl;
            }
        };
        
        class FruitBuyer: public BuyerImpl{
        public:
        
            virtual void buy(){
                cout << "buy fruit" << endl;
            }
        };
        
        class FoodBuyer: public BuyerImpl{
        public:
            virtual void buy(){
                cout << "buy food" << endl;
            }
        };
        
        class Buyer{
        protected:
            BuyerImpl *buyer;  //由于各修饰类中均包含了BuyerImpl,我们可以将其网上提
        public:
            Buyer(BuyerImpl *buyer):buyer(buyer){}
            virtual void order()=0;
        };
        
        class BuyerLite: public Buyer{  //无关平台的非会员的业务
        public:
            BuyerLite(BuyerImpl *buyerImpl):Buyer(buyerImpl){}
        
            virtual void order(){
                cout << "lite account" << endl;
                buyer->buy();
            }
        };
        
        class BuyerPerfect: public Buyer{ //无关平台的会员业务
        public:
            BuyerPerfect(BuyerImpl *buyerImpl): Buyer(buyerImpl){}
        
            virtual void order(){
                cout << "perfect account" << endl;
                buyer->buy();
            }
        };
        
        int main() {
            ClothesBuyer *clothesBuyer = new ClothesBuyer();
            BuyerLite *buyerLite = new BuyerLite(clothesBuyer);
            buyerLite->order();
            return 0;
        }
        
      • 这里我们将会员与非会员作为主维度,然后将具体的平台以组合的方式嵌入

  3. 类图

    image-20220317110608067

    注:

    • Abstraction和Implementor分别代表了两个维度的变化,其中Abstraction代表了主维度
    • 注意中间的连线是
  4. 定义:将业务部分(抽象部分)与平台实现(实现部分)分离,使它们可以独立地变化

  5. 注:

    • 平台实现与业务抽象分开
    • 业务实现通过继承实现
    • 平台抽象通过组合实现
    • 如果有多个维度的变化,都可以作为impl组合

(三)、对象创建

工厂模式(Factor Method)

  1. 背景:

    • 人要抚摸小动物,小动物产生不同声音反馈
    • 目前只摸猫和狗,后续可能添加其它动物
  2. code

    • main.cpp

      #include <iostream>
      #include "person.h"
      
      int main() {
          PetFactory *petFactory = new DogFactory();
          Person p(petFactory);
          p.touchAnimal();
          return 0;
      }
      
    • Person.h

      //
      // Created by Arno_vc on 2022/3/15.
      //
      
      #ifndef DESIGNMODE_PERSON_H
      #define DESIGNMODE_PERSON_H
      
      #include "PetFactory.h"
      
      class Person {
      public:
          PetFactory *petFactory;  //每个person有一个唯一的pet
          Person(PetFactory * petFactory){
              this->petFactory = petFactory;
          }
      
          void touchAnimal(){
              Pro *pet = petFactory->buildPro();
              pet->howl();
          }
      };
      
      #endif //DESIGNMODE_PERSON_H
      
    • PetFactory.h

      //
      // Created by Arno_vc on 2022/3/15.
      //
      
      #ifndef DESIGNMODE_PETFACTORY_H
      #define DESIGNMODE_PETFACTORY_H
      
      #include "Pro.h"
      
      class PetFactory {
      public:
          virtual Pro* buildPro()=0;
      };
      
      class DogFactory:public PetFactory{
      public:
          Pro* buildPro() override{
              return new DogPro();
          }
      };
      
      class CatFactory:public PetFactory{
      public:
          Pro* buildPro() override{
              return new CatPro();
          }
      };
      
      #endif //DESIGNMODE_PETFACTORY_H
      
    • Pro.h

      //
      // Created by Arno_vc on 2022/3/15.
      //
      
      #ifndef DESIGNMODE_PRO_H
      #define DESIGNMODE_PRO_H
      
      #include <string>
      #include <iostream>
      
      using namespace std;
      
      class Pro {
      protected:
          string name;
      public:
          virtual void howl()=0;
      };
      
      class CatPro:public Pro{
      public:
          void howl() override{
              cout << "miao miao miao" << endl;
          }
      };
      
      class DogPro:public Pro{
      public:
          void howl() override{
              cout << "wang wang wang" << endl;
          }
      };
      
      #endif //DESIGNMODE_PRO_H
      
    • 注:这里有一个前提的条件,那就是Pro的子类应该在Person中实例化,否则我们只需在Person中留下一个Pro *p;的指针即可,根本不需要工厂模式。更好的说法是如果对象的实例化分散在各种逻辑代码之间,那么就会非常零乱、难以维护,bug自然也多。

  3. 类图

    image-20220315112908894

    注:可以看到Person只和抽象类部分有关

  4. 定义:特点:绕开new,通过一个方法返回对象,定义一个用于创建对象的接口(基础工厂),让子类(分类工厂)决定实例化哪个类

抽象工厂(Abstract Factor)

  1. 背景

    • 我们有不同的宠物对象,如猫,狗等
    • 对于每个宠物有三个相关的对象,即宠物商店(假设每种宠物对应不同的宠物商店),宠物,宠物窝
  2. code

    • 对于变化的三个部分,尝试用工厂模式创建

      • main.cpp

        #include <iostream>
        #include <vector>
        
        using namespace std;
        
        //动物相关基类
        class Pet{
        
        };
        
        class PetShop{
        protected:
            vector<Pet *> petV;
        public:
            virtual void addPet(Pet *)=0;
        };
        
        class PetHouse{
        
        };
        //工厂相关基类
        class PetFactory{
        public:
            virtual Pet* build()=0;
        };
        
        class PetHouseFactory{
        public:
            virtual PetHouse* build()=0;
        };
        
        class PetShopFactory{
        public:
            virtual PetShop* build()=0;
        };
        
        class Dog: public Pet{
        
        };
        
        class DogFactory:public PetFactory{
        public:
            Dog* build(){
                return new Dog();
            }
        };
        
        class DogShop:public PetShop{
        
            virtual void addPet(Pet* pet){
                cout << "add a dog" << endl;
                petV.push_back(pet);
            }
        };
        
        class DogShopFactory:public PetShopFactory{
            DogShop* build(){
                return new DogShop();
            }
        };
        
        class DogHouse:public PetHouse{
        
        };
        
        class DogHouseFactory:public PetHouseFactory{
            DogHouse* build(){
                return new DogHouse();
            }
        };
        
        int main() {
            PetShopFactory *petShopFactory = new DogShopFactory();
            PetFactory *petFactory = new DogFactory();
            PetHouseFactory *petHouseFactory = new DogHouseFactory();
            //通常来说Factory为根据外部需求传入的参数,这里为演示方便创建
            PetShop *petShop = petShopFactory->build();
            Pet *pet = petFactory->build();
            petShop->addPet(pet);
            PetHouse *petHouse = petHouseFactory->build();
            return 0;
        }
        
      • 存在问题:需要传入的三个参数:PetShop,Pet,PetHouse应该是有关联的,当前的模式下无法体现三者的关联性

    • 修改后

      • main.cpp

        #include <iostream>
        #include <vector>
        
        using namespace std;
        
        //动物相关基类
        class Pet{
        
        };
        
        class PetShop{
        protected:
            vector<Pet *> petV;
        public:
            virtual void addPet(Pet *)=0;
        };
        
        class PetHouse{
        
        };
        
        class DogShop:public PetShop{
        
            virtual void addPet(Pet* pet){
                cout << "add a dog" << endl;
                petV.push_back(pet);
            }
        };
        
        class Dog: public Pet{
        
        };
        
        class DogHouse:public PetHouse{
        
        };
        
        //工厂相关基类
        class PetFactory{
        public:
            virtual PetShop* petShopBuild()=0;
            virtual Pet* petBuild()=0;
            virtual PetHouse* petHouseBuild()=0;
        };
        
        class DogFactory:public PetFactory{
        public:
            virtual PetShop* petShopBuild(){
                return new DogShop();
            }
            virtual Pet* petBuild(){
                return new Dog();
            }
            virtual PetHouse* petHouseBuild(){
                return new DogHouse();
            }
        };
        
        int main() {
            PetFactory *petFactory = new DogFactory();
            //通常来说Factory为根据外部需求传入的参数,这里为演示方便创建
            PetShop *petShop = petFactory->petShopBuild();
            Pet *pet = petFactory->petBuild();
            petShop->addPet(pet);
            PetHouse *petHouse = petFactory->petHouseBuild();
            return 0;
        }
        
      • 在一个抽象工厂中可以生产三个相关联的产品

  3. 类图

    image-20220317141826903
  4. 定义:将工厂抽象化,负责一系列相关对象的生产(即内部包含关联的子工厂)

  5. 注:

    • 用于"一系列相互依赖的对象"的创建工作
    • 工厂之间存在联系(实际上只是根据需求做了一些细微的变化罢了)
    • 常见应用于对不同数据库的操作(因为一个数据库操作中包含了多个相关联的数据库对象)

原型模式(Prototype Method)

  1. 背景

    • 我们现在有5个苹果
    • 现在3个苹果要拿出来清洗, 削皮,切片,然后吃了
  2. code

    • 构建5个苹果,将其中3个的清洗标志位置1

      • main.cpp

        #include <iostream>
        #include <vector>
        
        using namespace std;
        
        class Fruit{
        public:
            virtual void eat()=0;
        };
        
        class Apple{
        public:
            int washed;
            int peel;
            int cut;
        
            Apple(){
                washed=0;
                peel=0;
                cut=0;
            }
        
            void eat(){
                cout << "eat apple" << endl;
            }
        };
        
        int main() {
            Apple a[5];
            a[1].washed=1;
            a[2].washed=1;
            a[3].washed=1;
        
            a[1].peel=1;
            a[2].peel=1;
            a[3].peel=1;
        
            a[1].cut=1;
            a[2].cut=1;
            a[3].cut=1;
        
            a[1].eat();
            a[2].eat();
            a[3].eat();
            return 0;
        }
        
      • 存在问题

        • 在参数较多的情况小看起来很臃肿
        • 看起来不是很聪明的样子
    • 使用原型对象创建三个已经清洗了的苹果

      • main.cpp

        #include <iostream>
        #include <vector>
        
        using namespace std;
        
        
        
        class Fruit{
        public:
            virtual Fruit* clone()=0;
            virtual void eat()=0;
        };
        
        class Apple: public Fruit{
        public:
            int washed;
            int peel;
            int cut;
            Apple(){
                washed=0;
                peel=0;
                cut=0;
            }
        
            virtual Fruit* clone(){
                return new Apple(*this);  //胜读拷贝,应该记住这个形式
            }
        
            virtual void eat(){
                cout << "eat apple" << endl;
            }
        
        };
        
        int main() {
            Apple *prototype = new Apple();  //这个原型不是拿来用的,仅仅是拿来拷贝的
            prototype->washed=1;
            prototype->peel=1;
            prototype->cut=1;
        
            Fruit *a1 = prototype->clone();
            Fruit *a2 = prototype->clone();
            Fruit *a3 = prototype->clone();
        
            a1->eat();
            a2->eat();
            a3->eat();
            return 0;
        }
        
        • 原型对象是拿来创建对象的不是拿来使用的
        • 原型对象可以修改
  3. 类图

    image-20220318094720702
  4. 定义:使用原型对象实例指定创建对象的种类,通过深度拷贝创建新的对象

  5. 注:

    • 要求"原型"拥有稳定的接口
    • 面对"某些结构复杂的对象"的创建工作
    • 拷贝构造:在c++中主要通过深度拷贝实现,在Java中可以通过序列化来实现
    • js的继承就是采用了近乎原型对象的模式

构建器模式(Builder Method)(不常用)

  1. 背景

    • 我们要构造三个水果:苹果,梨,葡萄
    • 它们都有外皮,果肉,核三个部分
    • 它们的外皮,果肉,核三个部分不完全相同
  2. code

    • 尝试用构造函数实现一般化的构造

      • main.cpp

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Peel{
        public:
            string name;
        };
        
        class Flesh{
        public:
            string name;
        };
        
        class Core{
        public:
            string name;
        };
        
        class Fruit{
        protected:
            virtual void buildPeel()=0;  //子类根据需要覆盖:如苹果,构建苹果皮
            virtual void buildFlesh()=0;
            virtual void buildCore()=0;
        public:
            Peel *peel;
            Flesh *flesh;
            Core *core;
            void Fruit(){  //构建失败,因为再构造函数中无法调用虚函数。原因,在初始化时,父类先于子类初始化,故父类无法调用子类覆盖的虚函数
                buildPeel();
                buildCore();
                buildFlesh();
            }
        };
        
        class ApplePeel:public Peel{
        
        };
        
        class AppleFlesh:public Flesh{
        
        };
        
        class AppleCore:public Core{
        
        };
        
        class Apple:public Fruit{
        protected:
            virtual void buildPeel(){
                    peel = new ApplePeel();
            }
            virtual void buildFlesh(){
                flesh = new AppleFlesh();
            }
            virtual void buildCore(){
                core = new AppleCore();
            }
        };
        
        int main() {
            Apple *apple = new Apple();
            apple->build();
            cout << apple->peel->name << endl;
            cout << apple->flesh->name << endl;
            cout << apple->core->name << endl;
            return 0;
        }
        
      • 错误原因:

        • 目标:在父类中实现一般化的构造过程
        • 构建失败,因为再构造函数中无法调用虚函数。原因,在初始化时,父类先于子类初始化,故父类无法调用子类覆盖的虚函数
    • 修改后

      • main.cpp

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Peel{
        public:
            string name;
        };
        
        class Flesh{
        public:
            string name;
        };
        
        class Core{
        public:
            string name;
        };
        
        class Fruit{
        public:
            virtual void buildPeel()=0;  //子类根据需要覆盖:如苹果,构建苹果皮
            virtual void buildFlesh()=0;
            virtual void buildCore()=0;
        public:
            Peel *peel;
            Flesh *flesh;
            Core *core;
        };
        
        class FruitBuilder{
            Fruit *fruit;
        public:
            FruitBuilder(Fruit *fruit):fruit(fruit){}
            void build(){  //构建失败,因为再构造函数中无法调用虚函数。原因,在初始化时,父类先于子类初始化,故父类无法调用子类覆盖的虚函数
                fruit->buildPeel();
                fruit->buildCore();
                fruit->buildFlesh();
            }
        };
        
        class ApplePeel:public Peel{
        
        };
        
        class AppleFlesh:public Flesh{
        
        };
        
        class AppleCore:public Core{
        
        };
        
        class Apple:public Fruit{
        protected:
            virtual void buildPeel(){
                    peel = new ApplePeel();
            }
            virtual void buildFlesh(){
                flesh = new AppleFlesh();
            }
            virtual void buildCore(){
                core = new AppleCore();
            }
        };
        
        int main() {
            Apple *apple = new Apple();
            FruitBuilder *fruitBuilder = new FruitBuilder(apple);
            cout << apple->peel->name << endl;
            cout << apple->flesh->name << endl;
            cout << apple->core->name << endl;
            return 0;
        }
        
      • 注:

        • 可以看到,对于每种水果构建的基础单元相同(Peel,Flesh,Core),故以此为动静分离点
        • 通过普通的build函数代替构造函数实现了构造函数
        • 使用组合的方法,将build部分作为单独的功能与Fruit中的内容分离
        • 与Template模式的区别:前者用于代码执行,后者用于类的构建
  3. 类图

    image-20220320102112143

    注:

    • Builder:对应Fruit
    • ConcreteBuilder:对应Apple
    • Director:对应FruitBuilder
  4. 定义:将对象的构建和表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)

  5. 、注:

    • 用于分步骤构建一个复杂的对象
    • 通常有一部分稳定,有一部分变化,类似Template模式。但是,Template模式主要用于一系列相似行为(更具有普遍性),Builder模式主要用于构造
    • 当类复杂的时候,可以将内容和构造分离;当类简单的时候,可以将内容和构造合并
    • 需要注意语言的区别

(四)、"对象性能"模式

处理面向对象带来的成本问题

单例模式(Singleton)

  1. 背景:有一个西瓜的时候,大家共享;没有切开或者吃完了,就再打开一个西瓜

  2. code

    • 线程不安全版本(单线程版本)

      • main.cpp

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Watermelon{
        private:
            Watermelon(){};
            Watermelon(const Watermelon &watermelon){};
        public:
            static Watermelon *instance;
            static Watermelon *getInstance();  //静态成员函数只能调用静态成员变量
        };
        
        //单线程版本
        Watermelon *Watermelon::getInstance() {
            if(instance==NULL){
                instance = new Watermelon();
            }
            return instance;
        }
        
        Watermelon *Watermelon::instance = NULL;  //静态成员变量需要在类外显示地初始化
        
        int main() {
            Watermelon *watermelon = Watermelon::getInstance();  //::用于限定范围
            return 0;
        }
        
      • 注:注意类中静态成员变量的使用

    • 线程安全版本(高代价)

      • main.cpp

        #include <iostream>
        #include <mutex>
        
        using namespace std;
        
        class Watermelon{
        private:
            Watermelon(){};
            Watermelon(const Watermelon &watermelon){};
        public:
            static Watermelon *instance;
            static Watermelon *getInstance();  //静态成员函数只能调用静态成员变量
        };
        
        //加锁版本
        Watermelon *Watermelon::getInstance() {
            Lock lock;
            if(instance==NULL){
                instance = new Watermelon();
            }
            return instance;
        }
        
        Watermelon *Watermelon::instance = NULL;  //静态成员变量需要在类外显示地初始化
        
        int main() {
            Watermelon *watermelon = Watermelon::getInstance();  //::用于限定范围
            return 0;
        }
        
      • 注:

        • lock是一个一般化的锁,而不应该仅仅把它当做c++中的锁。该锁会在函数运行完成后释放
        • 无论是否需要创建新对象,代价较高。更好地方案是当确定需要创建新对象时上锁
    • 线程安全版本(双检查锁,内存读写reorder不安全)

      • main.cpp

        #include <iostream>
        #include <mutex>
        
        using namespace std;
        
        class Watermelon{
        private:
            Watermelon(){};
            Watermelon(const Watermelon &watermelon){};
        public:
            static Watermelon *instance;
            static Watermelon *getInstance();  //静态成员函数只能调用静态成员变量
        };
        
        //加锁版本
        Watermelon *Watermelon::getInstance() {
            if(instance==NULL){
                Lock lock;
                if(instance==NULL){
                    instance = new Watermelon();
                }
            }
            return instance;
        }
        
        Watermelon *Watermelon::instance = NULL;  //静态成员变量需要在类外显示地初始化
        
        int main() {
            Watermelon *watermelon = Watermelon::getInstance();  //::用于限定范围
            return 0;
        }
        
      • 注:

        • 为什么内部还要做一次判断:若不做判断,A线程刚拿到锁,B进来判断instance没有初始化,进来等待锁,那么instance对象就会被创建两次
        • 内存reorder读写不安全问题
          • 编译器在编译时会对源代码进行优化
          • 在本例中,new对象应该做的三个步骤:
            • 申请内存空间
            • 对空间初始化
            • 返回头指针
        • 经过reorder优化后的步骤
          • 申请内存空间
          • 返回头指针
          • 对空间初始化
        • 导致的问题:在空间没有初始化的情况下,先返回了头指针,导致该指针被错误地使用
    • 编译器本身优化

      • Java,C#:volatile关键字,告诉编译器不需要优化

      • c++ 11版本之后:通过atomic库

        std::atomic<Siingleton> Singleton::m_instance;
        std::mutex Singleton::m_mutex;
        
        Singleton* Singleton::getInstance(){
            Singleton* tmp = m_instance.load(std::memory_order+relaxed); //load
            std::atomic_thread_fence(std::memory_order_acquire);  //获取内存fence
            if(tmp == nullptr){
                std::lock_guard<std::mutex> lock(m_mutex);
                tmp = m_instance.load(std::memory_order_relaxed);
                if(tmp == nullptr){
                    tmp = new Singleton;
                    std::atomic_thread_fence(std::memory_order_release);  //释放内存fence
                    m_instance.store(tmp,std::memory_order_relaxed);  //store
                }
            }
            return tmp;
        }
        
  3. 类图:

    image-20220320115528098
  4. 定义:保证一个类仅有一个实例,并提供一个该实例的全局访问点

  5. 注:

    • 一个类只有一个实例。是类的设计者的责任,而不是使用者的责任。
    • 绕过构造器,实现性能优化。(在之前也有绕过构造器,但是是为了解决紧耦合问题)
    • 需要考虑线程安全
    • 屏蔽构造构造函数和拷贝构造函数(将其私有化)

享元模式(Flyweight)

  1. 背景

    • 有许多水果
    • 我们要记录水果的类别
    • 显然水果的类别与水果的关系是一对多的
  2. code

    使用享元实现

    • main.cpp

      #include <iostream>
      #include <map>
      
      using namespace std;
      
      class Fruit{
      public:
          string key;  //以水果类别作为标识
          Fruit(string key):key(key){}
      };
      
      class FruitFactory{
          map<string,Fruit*> mp;
      public:
          Fruit *getFruit(const string key){
              map<string,Fruit*>::iterator item = mp.find(key);  //注意这里用迭代器做判断更具有一般性
              if(item!=mp.end()){
                  return item->second;
              }else{
                  Fruit *value = new Fruit(key);
                  mp[key] = value;
                  return value;
              }
      
          }
      };
      
      int main() {
          FruitFactory fruitFactory;
          Fruit *apple = fruitFactory.getFruit("apple");
          cout << apple->key << endl;
          apple = fruitFactory.getFruit("apple");
          cout << apple->key << endl;
          return 0;
      }
      
  3. 类图

    image-20220320123045364
  4. 定义:运动共享技术有效地支持大量细粒度的对象

  5. 注:

    • 集合了工厂模式与单例模式:判断工厂中是否有key(而不是单例模式中判断引用),有则给,没有则创建
    • 一般用于只读
    • 常用于上万的数据量

(五)、接口隔离模式

接口直接依赖存在问题,采用一层间接(稳定)接口

门面模式(Facade Method)

  1. 背景:
    • 到商店去吃水果捞
    • 我不关心水果l捞是如何制作的
    • 因为到我面前的始终是一盆水果捞(接口)
  2. Code:无特定的代码结构,重点是作为一种设计思想去理解
  3. 类图:无
  4. 定义:为子系统中的一组接口提供一个一致(稳定)的高层接口,使得子系统更加容易使用
  5. 注:松耦合,高内聚

代理模式(Proxy Method)

  1. 背景

    • 吃水果捞:自己买水果,商店帮忙做水果捞
    • 我们可以做一些附加的额外操作:做成水果捞前自己挑水果,做成水果捞后可以自己加点奶昔
  2. code

    • 修改前

      • main.cpp

        #include <iostream>
        #include <map>
        
        using namespace std;
        
        class doFruityMixInf{
            virtual void doFruityMix()=0;
        };
        
        class FruityMix{  //这一部分内容对用户来说是屏蔽的
        public:
            void doFruityMix(){
                cout << "do fruitymix" << endl;
            }
        };
        
        int main() {
            cout << "buy fruit" << endl;
            FruityMix *fruityMix = new FruityMix();
            fruityMix->doFruityMix();
            cout << "add mike shake" << endl;
            return 0;
        }
        
      • 注:

        • 我们无法直接在业务代码添加附加的操作
        • 然而,实际上买水果和添加奶昔都是做水果捞时间的一部分,三者应该放在同一个代码块中以满足高内聚,低耦合的要求
    • 修改后(静态代理)

      • main.cpp

        #include <iostream>
        #include <map>
        
        using namespace std;
        
        class FruityMixInf{
        public:
            virtual void doFruityMix()=0;
        };
        
        class FruityMix:public FruityMixInf{  //这一部分内容对用户来说是屏蔽的
        public:
            virtual void doFruityMix(){
                cout << "do fruitymix" << endl;
            }
        };
        
        class FruityMixProxy:public FruityMixInf{  //与readObject继承同一个接口
        protected:
            FruityMixInf *fruityMixInf;
        public:
            FruityMixProxy(FruityMixInf *fruityMixInf):fruityMixInf(fruityMixInf){};
        
            virtual void doFruityMix(){
                cout << "buy fruit" << endl;
                fruityMixInf->doFruityMix();
                cout << "add mike shake" << endl;
            }
        };
        
        int main() {
            FruityMixInf *fruityMixInf = new FruityMix();
            FruityMixProxy *fruityMixProxy = new FruityMixProxy(fruityMixInf);
            fruityMixProxy->doFruityMix();
            return 0;
        }
        
      • 注:

        • 原对象和代理对象都继承了相同接口
        • 代理对象调用原对象,同时完成了一些附加操作
    • 动态代理

      • 显然,当有多个类的情况下,静态代理就会显得很繁琐,故可以使用动态代理只写一次代理操作(这里使用Java的反射机制举例)

      • Code

        • 尝试使用反射对一个函数进行包装

          package org.example;
          
          import org.omg.CORBA.portable.InvokeHandler;
          import redis.clients.jedis.Jedis;
          
          import java.lang.reflect.*;
          
          /**
           * Hello world!
           *
           */
          
          interface DemoInf{
              public void doWork();
          }
          
          class Demo implements DemoInf{
              public void doWork(){
                  System.out.println("Do something~");
              }
          }
          
          public class App
          {
              public static void main( String[] args ) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
                  Demo demo = new Demo(); //这里是唯一创建的实例,而不是接口
                  Class<?> proxyClass = Proxy.getProxyClass(DemoInf.class.getClassLoader(), DemoInf.class);
                  Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
                  DemoInf DemoInf = (DemoInf)constructor.newInstance(new InvocationHandler() {
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          System.out.println("before methods");
                          method.invoke(demo, args);  //通过Method类的invoke方法调用,实际的Demo对象
                          System.out.println("after methods");
                          return null;
                      }
                  });
                  DemoInf.doWork();
              }
          }
          

          注:

          • 这里调用了两个内容:ProxyInvocationHandler
            • Proxy:通过getProxyClass接口,用DemoInfClass生成包含DemoInfProxy对象的Classs(用Class生成Class,需要了解的是在JVM中Class是每各类唯一的状态描述对象),进而调用构造函数生成DemoInf的代理对象DemoProxy
            • InvocationHandler:后续,我们将通过DemoProxy调用函数。而实际上,DemoProxy调用的是内部InvocationHandler对象的接口下的invoke函数。通过对invoke函数覆写,我们可以实现AOP。
        • 将代理函数一般化

          package org.example;
          
          import org.omg.CORBA.portable.InvokeHandler;
          import redis.clients.jedis.Jedis;
          
          import java.lang.reflect.*;
          
          /**
           * Hello world!
           *
           */
          
          interface DemoInf{
              public void doWork(String s);
          }
          
          class Demo implements DemoInf{
              public void doWork(String s){
                  System.out.println("Do something:" + s);
              }
          }
          
          public class App
          {
              public static Object getProxy(Object demo) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
                  Class<?> proxyClass = Proxy.getProxyClass(demo.getClass().getClassLoader(), demo.getClass().getInterfaces());  //注意第二个参数获得了getClass的Interfaces
                  Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
                  Object res =  constructor.newInstance(new InvocationHandler() {
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          System.out.println("AOP:before method");
                          method.invoke(demo,args);  //因为我们这里添加了参数
                          System.out.println("AOP:after method");
                          return null;
                      }
                  });
                  return res;
              }
          
              public static void main( String[] args ) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
                  Demo demo = new Demo();
                  DemoInf proxy = (DemoInf) getProxy(demo);
                  proxy.doWork("sleep"); //proxy.doWork(args) => invoke(demo,args)
              }
          }
          

          注:

          • 这里注意一下可以通过class获得所有interface
          • invoke中添加args可以调用参数
          • 本质:和目标对象实现相同接口的实例
  3. 类图

    image-20220320135818925
  4. 定义:为其他对象提供一种代理以控制(隔离,使用接口)对这个对象的访问

  5. 注:

    • 为对象做一些附加的,额外的操作,如日志,安全控制等
    • 在实际使用时具有较高的复杂性
    • 不一定要求保持接口完整的一致性,在具体实现上主要表现为加一层
    • 尤其是在分布式上采用较多
    • 可以分为静态代理与动态代理

适配器模式(Adapter Method)

  1. 背景

    • 做水果捞
    • 原先,我们给自己做水果捞,过程可以分为洗水果,削皮,切片等步骤(老接口)
    • 现在,我们给顾客做水果捞,过程呈现给顾客的只有到手的水果(老接口)
  2. code

    • main.cpp

      #include <iostream>
      #include <map>
      
      using namespace std;
      
      class OldFruityMixInf{
      public:
          virtual void washFruit()=0;
          virtual void peelFruit()=0;
          virtual void sliceFruit()=0;
      };
      
      class FruityMix:public OldFruityMixInf{  //这一部分内容对用户来说是屏蔽的
      public:
          virtual void washFruit(){
              cout << "wash fruit" << endl;
          }
      
          virtual void peelFruit(){
              cout << "peel fruit" << endl;
          }
      
          virtual void sliceFruit(){
              cout << "slice fruit" << endl;
          }
      };
      
      class NewFruityMixInf{
      public:
          virtual void doFruit()=0;
      };
      
      class Adapter:public NewFruityMixInf{
          OldFruityMixInf *oldFruityMixInf;
      public:
          Adapter(OldFruityMixInf *oldFruityMixInf):oldFruityMixInf(oldFruityMixInf){}
      
          virtual void doFruit(){
              oldFruityMixInf->washFruit();
              oldFruityMixInf->peelFruit();
              oldFruityMixInf->sliceFruit();
          }
      };
      
      int main() {
          FruityMix *fruityMix = new FruityMix();
          NewFruityMixInf *newFruityMixInf = new Adapter(fruityMix);
          newFruityMixInf->doFruit();
          return 0;
      }
      
    • 注:对旧的接口进行封装,做成新的接口

  3. 类图

    image-20220322102840395
  4. 定义:将一个类的接口转换成客户希望的另一个接口

  5. 注:

    • 适配器本身的作用也是移植不同接口的匹配需求
    • 继承新接口,组合旧接口

中介者模式(Mediator Method)(不常用)

  1. 背景

    • 依赖关系

      image-20220322105412072
    • 使用适配器

      image-20220322105553754

  2. Code:无

  3. 类图

    image-20220322110008985
  4. 定义:用一个中介对象来封装(封装变化)一系列的对象交互。中介者使各对象不需要显式的相互引用(编译时依赖=>运行时依赖),从而使其耦合松散(管理变化),而且可以相互独立地改变它们之间的交互

  5. 注:

    • 与Facade模式的区别:Facade注重区分系统内和系统外的元素,Mediator模式注重区分系统内的元素
    • 作用是将一组元素之间复杂的依赖关系通过一个专门的中介去梳理
    • 需要自己定义Mediator中具体的调用协议

(六)、“状态变化“模式

组件构建过程中某些对象的状态经常面临变化,又维持高层模块的稳定

状态模式(State Method)

  1. 背景

    • 做水果捞
    • 水果捞的几种状态:原生状态,洗好,剥皮,切开,加奶昔
  2. code

    • 修改前

      • main.cpp

        #include <iostream>
        #include <map>
        
        using namespace std;
        
        enum State{
            original,wash,peel,slice,over
        };
        
        class FruityMixInf{
        public:
            virtual void operation()=0;
        };
        
        class FruityMix:public FruityMixInf{  //这一部分内容对用户来说是屏蔽的
            State state;
        public:
            FruityMix(){
                state = original;
            }
            virtual void operation(){
                if(state == original){
                    cout << "wash" << endl;
                    state = wash;
                }else if(state == wash){
                    cout << "peel" << endl;
                    state = peel;
                }else if(state == peel){
                    cout << "slice" << endl;
                    state = slice;
                }else if(state == slice){  //注意add shake对应over状态
                    cout << "add Shake" << endl;
                    state = over;
                }
            }
        };
        
        int main() {
            FruityMix fruityMix;
            fruityMix.operation();
            fruityMix.operation();
            fruityMix.operation();
            fruityMix.operation();
            return 0;
        }
        
      • 注:当我们想要添加一个wait状态时不符合开闭原则

    • 修改后

      • main.cpp

        #include <iostream>
        #include <map>
        
        using namespace std;
        
        class State{
        public:
            virtual State* operation()=0;
        };
        
        class Over:public State{
        public:
            virtual State* operation(){
                cout << "over" << endl;
                return NULL;
            }
        };
        
        class Slice: public State{
        public:
            virtual State* operation(){
                cout << "add shake" << endl;
                return new Over();
            }
        };
        
        class Peel: public State{
        public:
            virtual State* operation(){
                cout << "slice" << endl;
                return new Slice();
            }
        };
        
        class Wash: public State{
        public:
            virtual State* operation(){
                cout << "peel" << endl;
                return new Peel();
            }
        };
        
        class Original: public State{
        public:
            virtual State* operation(){
                cout << "wash" << endl;
                return new Wash();
            }
        };
        
        class FruityMixInf{
        public:
            virtual void operation()=0;
        };
        
        class FruityMix:public FruityMixInf{  //这一部分内容对用户来说是屏蔽的
            State *state;
        public:
            FruityMix(State *state):state(state){}
            virtual void operation(){
                state = state->operation();
            }
        };
        
        int main() {
            State *original = new Original();
            FruityMixInf *fruityMix = new FruityMix(original);
            fruityMix->operation();
            fruityMix->operation();
            fruityMix->operation();
            fruityMix->operation();
            fruityMix->operation();
            return 0;
        }
        
      • 注:增加一个状态wait,只需要添加对应的状态类即可

  3. 类图

    image-20220322125129252
  4. 定义:允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。

  5. 注:

    • 与策略模式相似,通常也是在有大量if-else语句的情况下使用,不同点在于通过一个对象表示状态转移
    • 虚函数:运行时的if-else语句
    • 满足开闭原则
    • 可以结合Singleton模式使用

Memento Method(备忘录设计模式)(不常用)

  1. 背景:

    • 使用A对象
    • 使用B对象记录A对象的状态
    • A对象出问题,用B对象回复A对象
    • 从行为上看,类似于windows的备份
  2. code

    class Memento{
        string state;
        //..
    public:
        Memento(const string &s):state(s){}
        string getState() const{return state;}
        void setState(const string & s){state=s;}
    }
    
    class Originator{
        string state;
    public:
        Originator(){}
        Memento createMemento(){
            Memento m(state);
            return m;
        }
        void setMemento(const Memento & m){
            state = m.getState();
        }
    }
    
    int main(){
        Originator originator;
        Memento mem = originator.createMemento();  //生成备忘录
        
        //... 改变originator状态
        
        originator.setMemento(mem);
    }
    
  3. 类图

    image-20220322140737708
  4. 定义:在不破坏封装性的前提下,捕捉一个对象的内部状态并在该对象之外保存这个状态,这样以后可以将该对象恢复到原先保存的转态

  5. 注:

    • 要求程序能回溯到对象之前处于某个点的状态(如果使用公有接口来让其它对象得到对象的状态,可能会暴露对象的细节实现)
    • 具体实现,备忘录的实现可以用字符流,内存流,序列化(Java)的方式存储
    • 核心
      • 信息隐藏
      • 保持封装性
    • 不用深拷贝:当对象内部存在指针是不太适合用深拷贝
    • 从某些地方看有点过时,主要了解其思想

(七)、”数据结构“模式

将特定数据结构封装在内部,在外部提供统一的接口

组合模式(Composite Method)

  1. 背景

    • 在一棵树中有composite(非叶节点)和leaf两个部分
    • 显然非叶节点可以向下遍历,叶节点不能向下遍历
  2. code

    • main.cpp

      #include <iostream>
      #include <string>
      #include <vector>
      
      using namespace std;
      
      class Component{
      public:
          virtual void process()=0;
          //virtual ~Component()=0;
      };
      
      class Composite:public Component{
          string name;
          vector<Component *> nodes;
      public:
          Composite(string name):name(name){}
          void addVector(Component *node){
              nodes.push_back(node);
          }
          virtual void process(){
              cout << name << endl;
      
              for(auto it:nodes){
                  it->process();
              }
          }
      };
      
      class Leaf:public Component{
          string name;
      public:
          Leaf(string name):name(name){}
          virtual void process(){
              cout << name << endl;
          }
      };
      
      int main() {
          Composite *c1 = new Composite("composite1");
          Composite *c2 = new Composite("composite2");
          Composite *c3 = new Composite("composite3");
          Composite *c4 = new Composite("composite4");
      
          Component *l1 = new Leaf("leaf1");
          Component *l2 = new Leaf("leaf2");
          Component *l3 = new Leaf("leaf3");
          Component *l4 = new Leaf("leaf4");
      
          c1->addVector(c2);
          c1->addVector(c3);
          c1->addVector(c4);
      
          c1->addVector(l1);
          c1->addVector(l2);
          c1->addVector(l3);
          c1->addVector(l4);
      
          c1->process();
      
          return 0;
      }
      
    • 注:

      • 可以看到composite和leaf公用一个process接口操作
  3. 类图

    image-20220322153946023
  4. 定义:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性(对待单个对象和组合对象一样的处理方式)

  5. 注:

    • 通过多态访问树结构
    • 核心:将“客户代码与复杂的对象容器结构”解耦
    • 应用:文件系统

迭代器模式(Iterator Method)(不常用)

  1. 背景:

    • 有多个不同的集合,Map,List等
    • 我们希望用同一个接口遍历
  2. code

    //这里只是 简单介绍一下面向对象的iterator接口
    class iterator{
    public:
        virtual void first()=0;
        virtual void next()=0;
        virtual bool isDone() const=0;
        virtual T& current()=0;
    }
    
  3. 类图

    image-20220322160328892
  4. 定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露(稳定)该对象的内部表示

  5. 注:

    • 和STL模板里的迭代器
      • 设计目的相同
      • 设计理念不同,gof提出的迭代器以面向对象为基础
    • 在某些地方显得过时
  6. c++的泛型编程通过模板的迭代器取代了面向对象的迭代器,因为面向对象使用虚函数是有额外的时间成本的(运行时多态),而模板则不会(编译时多态且基于隐式约束)

  7. java等语言依然是基于面向对象的迭代器

职责链模式(Chain of Resposibility)(不常用)

  1. 背景:

    • ui界面多组件的重叠下,点击事件的响应
  2. code:无

  3. 类图

    image-20220322163752066
  4. 定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止

  5. 注:

    • 构成了一条链表
    • 应用:一个请求可能有多个接收者,但最后真正的接收者只有一个
    • 看上去有点过时
    • 应该有缺省机制

(八)、“行为变化”模式

将组件的行为和组件本身进行解耦,从而支持组件行为的变化

命令模式(Command Method)(不常用)

  1. 背景:

    • 在文档中我们做了剪切,粘贴,复制等操作
    • 我们需要将这些操作保存起来,以方便redo和undo
  2. code

    class Command{
    public:
        virtual void execute()=0;  //通过虚函数进行实现
    }
    
  3. 类图

    image-20220322170836762
  4. 定义:将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作

  5. 注:

    • 用对象来表征行为(一段代码)
    • 好处
      • 可以作为参数传递
      • 可以存储在数据结构中
      • 可以做序列化
    • 可以通过使用Composite模式,将多个“命令”封装为一个“符合命令”MacroCommand
    • 与c++函数对象
      • 都是将函数对象化
      • Command基于虚函数,接口规范严格,有性能损失;函数对象更灵活,性能更高
      • 与Iterator Method模式相同,在Java等语言得到应用

“访问器”模式(Visitor)(慎用)

  1. 背景:

    • 基类ElementInf,子类\(Elment_1,Elment_2,Elment_3\)
    • 新需求,在源代码基础上给Element添加虚函数fun2,是\(Element_1,Element_2,Element_3\)实现
  2. code

    • 修改前:直接在源代码基础上添加则违反了开闭原则

    • 修改后

      • main.cpp

        //由于本地ide激活码没了,暂时跑不了
        #include <iostream>
        
        using namespace std;
        
        class Visit{
            virtual visitElement1(ElementInf *e)=0; 
            virtual visitElement2(ElementInf *e)=0;
        }
        
        class Visit1{  //第一个附加函数
            virtual visitElement1(ElementInf *e){
                cout << "This Element1, fun1" << endl;
            }
            virtual visitElement2(ElementInf *e){
                cout << "This Element2, fun1" << endl;
            }
        }
        
        class ElementInf{
            virtual void fun1()=0;
            virtual void accept(Visit *v)=0;
        }
        
        class Element1{
            virtual void fun1{
                cout << "Element1 fun1";
            }
            virtual void accept(Visit *v){
                v->visitElement1(this);
            }
        }
        
        class Element2{
            virtual void fun1{
                cout << "Element1 fun1";
            }
            virtual void accept(Visit *v){
                v->visitElement2(this);
            }
        }
        
        int main(){
            Visit *visit1 = new Visit1();
            ElementInf *e1 = new Element1();
            e1.accept(v1);
            return 0;
        }
        
        • 每个visit的子类相当于一个扩充的虚函数
        • visit中每个函数对应一个具体的类
  3. 类图

    image-20220322202816137
  4. 定义:表示一个作用于某对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)

  5. 注:

    • Element类层次结构稳定,操作却经常面临频繁改动

    • 当基类继承的每个子类都需要添加某个动作时适合使用该模式;当仅仅个别子类需要增加操作,直接在子类上添加动作即可

    • 用得很少,但一旦用整个结构就会变得很笨重

    • 原来是在每个子类中添加一个同名函数 => 新建一个类作为函数的执行类,传入应该执行的类执行对应的方法(Elment1 => fun1)

      重点:原函数中有留下一个函数接口accept来接受访问类

(九)、“领域规则”模式

结合特定领域,将问题抽象为规则

解析器(Interpreter)(不常用)

  1. 背景:a+b-c+d

  2. code(麻烦但不复杂,写一下思路)

    • 定义节点:父节点 => 变量节点,符号节点 => 加法,减法节点
    • 解析表达式生成符号树
    • 自上而下,递归解析符号树,各节点调用自身的虚函数
  3. 类图

    image-20220322210702400
  4. 定义:给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子

  5. 注:

    • 通常结合虚函数进行表达
    • 选择应用场景是难点:满足“业务规则频繁变化,且类似的结构不断重复出现,并且容易抽象为语法规则的问题”,如简单的语法树

三、简述

  1. 模板方法:一段代码中有稳定的部分,也有变化的部分。将稳定的部分作为一个类中的run函数,将变化的部分写作几个函数,供run函数调用

  2. 策略模式:针对if-else语句块的一般性优化,可以搭配工厂模式使用

  3. 观察者模式:解耦事件与被通知对象,将被通知对象保存在类的vector等容器中

  4. 装饰者模式:使用继承,能够以一种任意的方式(附加操作)向一个对象添加特征

  5. 桥模式:使用接口+组合(在c++中表现为多继承),以不同维度(非附加操作)指定一个对象

    补充装饰者模式与桥模式的区别:

    • 装饰者模式通常是用于添加额外的功能(咖啡有不同的品种,还有加不加奶,加不加糖等选项)
    • 桥模式通常与搭配选择特定的选项(咖啡的不同品种——主模式,也有不同大小杯——额外的模式)
  6. 简单工厂模式(查询资料后附加):在创建对象是我们可能会有一些附加操作,比如查询数据库。如果直接写在构造函数中会变得很臃肿,所以我们可以使用工厂中的一个函数创建。一个工厂可能生产不同的产品,我们可以在方法中传入参数string type区别,if-else语句进行区分创建(违反了开闭原则,故提出了下面的工厂方法模式)

  7. 工厂方法模式:基于某个初始状态创建对象,主要目的在于解耦

  8. 抽象工厂模式:将存在关联性的几个工厂合并为一个工厂

  9. 原型模式:基于某个中间状态对象创建对象

  10. 构建器模式:与Template模式相似,但前者用于代码执行,后者用于类的构建

  11. 单例模式:一个系统中最多只有一个该类

  12. 享元模式:使用类似key-value的模式,避免创建相同key的类(只读)

  13. 门面模式:注重区分系统内和系统外的元素,抽象没有固定代码

  14. 代理模式:额外加一层,附加操作,如日志,网络连接等

  15. 适配器模式:额外加一层,用老接口适配新接口

  16. 中介者模式:额外加一层,注重区分系统内部元素间的关系

  17. 状态模式:针对if-else语句块的分状态讨论的优化,通常这些状态时递进的关系,解决方法和策略模式相同,都是抽象为类

  18. 备忘录模式:类似系统备份,目前实现方法可以用字符流,内存流,序列化等形式

  19. 组合模式:使用多态遍历树结构

  20. 迭代器:常见于STL模板

  21. 职责链:就是把事件可能响应的对象串成一条链

  22. 命令模式:函数对象

  23. 访问器模式:代码维护阶段,父类需要添加虚函数,子类需要实现,违反开闭原则,通过该方式可以解决(臃肿,慎用)

  24. 解析器模式:用于解析一些简单的语法规则

四、补充

  1. 虚函数:延迟到运行时
  2. 以上的模式分类是按照视频作者:还需将其与三种经典的分类方法比较
  3. 子类将有相同的字段,可以往上提到父类中
  4. 思考设计模式从两个角度
    • 设计模式的背景
    • 这个与相似设计模式的区别
  5. 读线程不需要加锁,有写线程就需要加锁
posted @ 2022-03-23 10:27  Arno_vc  阅读(48)  评论(0编辑  收藏  举报