为您推荐:
精华内容
最热下载
问答
  • 5星
    3.77MB GZXGYZ 2021-08-16 09:49:24
  • 5星
    8.51MB GZXGYZ 2021-08-16 10:12:36
  • 1. 设计模式分类 2. 好的面向对象设计 2.1. 三大面向对象机制 2.2. 重构技法 2.3. 软件设计需要解决的因素 3. 设计原则 4. 创建型模式 4.1. 单例模式 4.1.1. 简介 4.1.2. 使用效果?优缺点? 4.1.3. 使用时注意...

    c++设计模式详解

    1. 设计模式分类

    设计模式一般有 23 种设计模式,可分为三类:

    • 创建型模式(Creational Patterns)
    • 结构型模式(Structural Patterns)
    • 行为型模式(Behavioral Patterns)

    设计模式概图

    2. 好的面向对象设计

    2.1. 三大面向对象机制

    • 封装:隐藏内部实现(创建型模式)
    • 继承:复用现有代码(结构型模式)
    • 多态:改写对象行为(行为型模式)

    2.2. 重构技法

    静态->动态
    早绑定->晚绑定
    继承->组合
    编译时依赖->运行时依赖
    紧耦合->松耦合

    2.3. 软件设计需要解决的因素

    产品需求的变化
    开发技术的升级换代
    技术团队的变更
    市场环境的改变

    设计模式应对的是稳定中的变化。
    一般来说,抽象基类是稳定,具体实现是变化的。

    3. 设计原则

    设计原则是设计模式的核心。

    1. 单一职责原则(Single Responsibility Principle)
      每个类应该实现单一的职责,否则就应该把类拆分。

    解读:每个类只做一件事,可以降低代码的耦合性,也方便代码的维护。实现新需求应注意不随意修改旧代码,注意职责的划分。

    1. 开-闭原则(Open Closed Principle)
      对外可以扩展,对内不能修改。

    解读:用抽象构建框架,用实现扩展细节。实现新需求不改动原有类,而是应该实现抽象出来的接口(或具体类继承抽象类)。

    1. 里氏替换原则(Liskov Substitution Principle)
      里氏替换原则主要是规范子类与基类(父类)继承的关系。

    里氏替换原则是对“开-闭原则”的补充。实现“开闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。

    解读:要求衍生类可以替换基类,软件单位的功能不受到影响,此时基类才能真正被复用。子类中可以增加自己特有的方法,也可以实现父类的抽象方法,但是不能重写或重载父类的非抽象方法,否则该继承关系就不是一个正确的继承关系。因为父类代表了定义好的结构,与外界交互,子类不应该随便破坏它。

    1. 依赖倒置原则(Dependence Inversion Principle)
      面向接口编程,依赖抽象而不依赖实现。

    解读:写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。针对接口编程,而不是针对实现编程。尽量不要从具体的类派生,而是以继承抽象类或实现接口来实现。一般业务层处于上层,逻辑层和数据层归为底层。

    1. 接口隔离原则(Interface Segregation Principle)
      每个接口不存在子类用不到却必须实现的方法。否则将接口拆分成多个隔离的接口。

    解读:软件不应该依赖不需要实现的接口。尽量细化接口,接口中的方法应尽量少。但也不能太少,如果过少会造成接口数量过多,设计复杂化。

    1. 迪米特法则(最少知道原则)(Demeter Principle)
      一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。

    解读:一个类应该只和它的成员变量,方法的输入,返回参数中的类作交流,而不应该引入其他的类。
    我们要求陌生的类不要作为局部变量出现在类中。最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。

    4. 创建型模式

    创建型模式用于构建对象,以便它们可以从实现系统中分离出来。主要有5种:

    • 单例模式(Singleton Pattern)
    • 工厂方法模式(Factory Method Pattern)
    • 抽象工厂模式(Abstract Factory Pattern)
    • 建造者模式(Builder Pattern)
    • 原型模式(Prototype Pattern)

    4.1. 单例模式

    4.1.1. 简介

    单例模式是指整个系统中一个类只有一个对象的实例。
    单例模式虽然简单,但有一个经典问题:如何保证多线程安全?

    1. 构造要点
    • 构造私有private
      如果要保证一个类不能多次被实例化,那么肯定要阻止对象被多次new出来,所以需要把类的所有构造方法私有化。
    • 以静态方法static返回实例
      因为外界不能通过new来获得对象,所以我们要通过提供类的方法GetInstance()来让外界获取对象实例。
    • 确保对象实例只有一个
      只对类进行一次实例化,以后都直接获取第一次实例化的对象。
    // 单例
    class Singleton
    {
    public:
       // 修改返回类型为指针类型
       static Singleton* GetInstance()
       {
           static Singleton instance;
           return &instance;
       }
    
    private:
       Singleton() {}
    };
    
    1. 解决问题
      实例对象保证只有一个。

    2. 分类
      单例模式一般分为懒汉模式,饿汉模式。

    // 单例 - 饿汉式
    Singleton *Singleton::m_pSingleton = new Singleton();
    
    Singleton *Singleton::GetInstance()
    {
       return m_pSingleton;
    }
    

    饿汉模式指先把对象创建好,等我要用时直接来拿就行。
    优点:最简单
    缺点:容易造成资源上的浪费(如:我把对象创建好了,但不用)

    // 单例 - 懒汉式
    Singleton *Singleton::m_pSingleton = NULL;
    
    Singleton *Singleton::GetInstance()
    {
       if (m_pSingleton == NULL)
           m_pSingleton = new Singleton();
    
       return m_pSingleton;
    }
    

    懒汉模式指先不创建类的对象实例,等需要时再创建。
    优点:解决饿汉模式可能会造成资源浪费的问题
    缺点:懒汉模式在并发情况下可能引起的问题,会出现创建多个对象的情况

    因为可能出现外界多人同时访问SingleCase.getInstance()方法,这里可能会出现因为并发问题导致类被实例化多次,所以懒汉模式需要加上锁synchronized (Singleton.class) 来控制类只允许被实例化一次。

    1. UML
      单例模式通用类图

    4.1.2. 使用效果?优缺点?

    优点:

    1. 单例模式可以在系统设置全局的访问点, 优化和共享资源访问。
    2. 由于单例模式在内存中只有一个实例, 减少了内存开支,减少了系统的性能开销,避免对资源的多重占用。

    缺点:

    1. 单例模式与单一职责原则有冲突。 一个类应该只实现一个逻辑, 而不关心它是否是单例的, 是不是要单例取决于环境, 单例模式把“要单例”和业务逻辑融合在一个类中。
    2. 单例模式一般没有接口, 扩展很困难。

    4.1.3. 使用时注意事项!

    单例模式在多线程中会出现问题

    Singleton *Singleton::GetInstance()
    {
       if (m_pSingleton == NULL)
           m_pSingleton = new Singleton();
    
       return m_pSingleton;
    }
    

    如何保证多线程安全?
    方法一:直接加锁

    Singleton *Singleton::GetInstance()
    {
      Lock lock;
       if (m_pSingleton == NULL)
           m_pSingleton = new Singleton();
    
       return m_pSingleton;
    }
    

    这种方法锁的代价过高,不管m_pSingleton是不是NULL,其他线程都会陷入等待锁的释放。

    方法二:双检查锁,由于内存读写reorder不安全
    构造对象的逻辑过程:开辟对象的内存空间->执行构造函数初始化的操作->返回指向对象的指针
    reorder指令执行的过程:开辟对象的内存空间->返回指向对象的指针—在这一步时—>执行构造函数初始化的操作
    当其他线程获取该对象时,可以获取对象的指针,但是没有执行构造函数初始化,就会在造成安全问题。
    内存读写reorder不安全这个问题是由编译器优化造成的,一度很难解决。
    c#,Java引入关键词volatile约束实例对象,让编译器不会reorder,按照顺序步骤进行。

    Singleton *Singleton::GetInstance()
    {
       if (m_pSingleton == NULL){
           Lock lock;
           m_pSingleton = new Singleton();
       }
       return m_pSingleton;
    }
    

    方法三:双检查锁,c++11版本之后的跨平台实现(c++中vc2005之后添加了volatia,但只能在windows中使用)

    std::atomic<Singleton*> Singleton::m_pSingleton;  //约束为原子对象
    std::mutex Singleton::m_mutex;
    
    Singleton *Singleton::GetInstance()
    {
      Singleton* tmp= m_pSingleton.load(std::memory_order_relaxed);
      std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
       if (tmp == NULL){
           std::lock_guarg<std::mutex> lock(m_mutex);
           tmp = m_pSingleton.load(std::memory_order_relaxed);
           if (tmp == NULL){
              tmp = new Singleton();    
              std::atomic_thread_fence(std::memory_order_release);//释放内存fence
              m_pSingleton.store(tmp,std::memory_order_relaxed);
           }
       }
       return m_pSingleton;
    }
    

    4.1.4. 使用场景?业界知名例子

    1. 像日志管理、打印机、数据库连接池、应用配置。
    2. Singleton 模式经常和 Factory( AbstractFactory) 模式在一起使用, 因为系统中工厂对象一般来说只要一个。
    3. 在整个项目中需要一个共享访问点或共享数据, 例如一个Web页面上的计数器, 可以不用把每次刷新都记录到数据库中, 使用单例模式保持计数器的值, 并确保是线程安全的。
    4. 创建一个对象需要消耗的资源过多, 如要访问IO和数据库等资源等。

    4.1.5. 难点

    单例模式看着很简单,但实现上却很复杂,如何做到单例模式下多线程安全是个难点,多线程编程需要认真学习。

    需要注意的一点是,上面讨论的线程安全指的是UML中的获取Instance()是线程安全的,假如多个线程都获取类A的对象,如果只是只读操作,完全OK,但是如果有线程要修改,有线程要读取,那么类A自身的函数需要自己加锁防护,不是说线程安全的单例也能保证修改和读取该对象自身的资源也是线程安全的。

    4.2. 工厂方法模式

    4.2.1. 简介

    1. 构造要点
    • 工厂类创建对象产品具体类
    • 使用面向对象的手法,将具体对象创建延迟到子类
    1. 解决问题
      为一类对象创建构造接口

    2. 举例
      现在有奔驰、宝马、奥迪工厂、分别生产奔驰、宝马、奥迪汽车

    3. UML
      工厂模式通用类图

    4.2.2. 使用效果?优缺点?

    优点:

    1. 工厂类扩展比较容易
    2. 具有封装性,不需要知道产品类是如何创建的,只要知道如何调用工厂类的接口去创建具体的产品即可

    缺点:

    1. 产品类扩展会很麻烦,违背开闭原则

    4.2.3. 使用场景?业界知名例子

    工厂方法模式还可以与其他模式混合使用,如模板方法模式、 单例模式、原型模式等。

    4.3. 抽象工厂模式

    4.3.1. 简介

    1. 构造要点
    • 抽象工厂与工厂模式类似,区别是应对多系列对象的构建
    • “系列对象”指某一特定系列下的对象之间有相互依赖、作用的关系
    • 不同系列的对象之间不能有相互依赖
    1. 解决问题
      为多类(相关)对象创建构造接口

    2. 举例
      现在有奔驰、宝马、奥迪工厂、分别生产奔驰、宝马、奥迪汽车和自行车

    3. UML
      抽象工厂模式通用类图

    4.3.2. 使用效果?优缺点?

    优点:

    1. 应对“新系列”的需求变动(工厂),工厂类扩展比较容易
    2. 具有封装性,不需要知道产品类是如何创建的,只要知道如何调用工厂类的接口去创建具体的产品即可

    缺点:

    1. 难以应对“新对象的需求变动”(产品),产品类扩展会很麻烦,违背开闭原则

    4.3.3. 使用场景?业界知名例子

    具体的工厂类可以使用单例模式。

    使用场景较多,如:在软件产品开发过程中, 涉及不同操作系统的时候,可以考虑使用抽象工厂模式。

    例如一个应用, 需要在三个不同平台(Windows、 Linux、
    Android(Google发布的智能终端操作系统) ) 上运行,此时通过抽象工厂模式屏蔽掉操作系统对应用的影响。

    三个不同操作系统上的软件功能、 应用逻辑、 UI都应该是非常类似的, 唯一不同的是调用不同的工厂方法, 由不同的产品类去处理与操作系统交互的信息。

    4.4. 建造者模式

    4.4.1. 简介

    1. 构造要点
    • Director对象并不直接返回对象,而是通过一步步(BuildPartA, BuildPartB, BuildPartC)来进行对象的创建
    • 分步骤构建对象,其中“分步骤”是稳定不变的,复杂对象的各个部分则经常变化
    • 产品不需要设置抽象类,每个步骤中具体操作都不同生成的产品差异巨大,没必要设置公共父类
    • 针对变化的各个部分进行封装
    • 在c++中不能在构造器中调用虚函数,java、c#则可以
    1. 解决问题
      分步骤构建对象,每个步骤里面的操作不同,就能构建不同的对象

    2. 举例
      组装 ThinkPad、Yoga电脑,都需要组装CPU、主板、内存 、显卡。
      同时CPU、主板、内存 、显卡可以选择不同的型号。

    3. UML
      建造者模式通用类图

    4.4.2. 使用效果?优缺点?

    优点:

    1. 封装性
      客户端不必知道产品内部组成的细节, 如例子中我们就不需要关心每一个具体的模型内部是如何实现的, 产生的对象类型就是ThinkPadModel、YogaModel。
    2. 建造者独立, 容易扩展
      ThinkPad、Yoga的建造者是相互独立的, 对系统的扩展非常有利。

    缺点:

    4.4.3. 使用场景?业界知名例子

    建造者模式经常会与模板方法模式结合。

    4.5. 原型模式

    4.5.1. 简介

    1. 构造要点
    • 通过克隆自己clone()创建对象,拷贝构造函数(深拷贝)
    • 创建对象的过程中需要保留中间状态,使用原型模式
    1. 解决问题
      “克隆”再创建
      即一个对象的产生可以不由零起步, 直接从一
      个已经具备一定雏形的对象克隆, 然后再修改为生产需要的对象

    2. 举例
      孙悟空复制出来成千上万的孙悟空

    3. UML
      原型模式通用类图

    4.5.2. 使用效果?优缺点?

    优点:

    1. 性能优良
      在需要频繁产生相似对象时,可以使用原型模式。

    缺点:

    4.5.3. 使用场景?业界知名例子

    组合模式和装饰器模式可以与原型模式结合使用。

    通过new产生一个对象需要非常繁琐的数据准备或访问权限,或类初始化需要消化非常多的资源(数据、 硬件资源等),可以使用原型模式。

    一个对象需要提供给其他对象访问, 而且各个调用者可能都需要修改其值时, 可以考虑使用原型模式拷贝多个对象供调用者使用。

    切忌不能浅拷贝!由于浅拷贝还是指向原生对象的内部元素地址,其对象内部的数组、 引用对象等都不拷贝。

    4.6. 创建型模式对比

    • 抽象工厂模式 VS 工厂模式

      AbstractFactory 模式是为创建一组( 有多类) 相关或依赖的对象提供创建接口
      Factory 模式是为一类对象提供创建接口或延迟对象的创建到子类中实现
      AbstractFactory 模式通常都是使用 Factory 模式实现

    • 原型模式和建造者模式、抽象工厂模式

      都是通过一个类(对象实例)来专门负责对象的创建工作(工厂对象)

      Builder 模式重在复杂对象的一步步创建(并不直接返回对象)
      AbstractFactory 模式重在产生多个相互依赖类的对象
      Prototype 模式重在从自身复制自己创建新类

    • 建造者模式 VS 抽象工厂模式
      功能相似,因为都是用来创建大的复杂的对象

      Builder 模式强调的是一步步创建对象,并通过相同的创建过程可以获得不同的结果对象,一般来说 Builder 模式中对象不是直接返回的
      AbstractFactory 模式中对象是直接返回的, AbstractFactory 模式强调的是为创建多个相互依赖的对象提供一个同一的接口

    5. 结构型模式

    结构型模式用于在许多不同的对象之间形成大型对象结构。主要有7种:

    • 适配器模式(Adapter Pattern)
    • 组合模式(Composite Pattern)
    • 装饰者模式(Decorator Pattern)
    • 桥接模式(Bridge Pattern)
    • 外观模式(Facade Pattern)
    • 享元模式(Flyweight Pattern)
    • 代理模式(Proxy Pattern)

    5.1. 适配器模式

    5.1.1. 简介

    1. 构造要点
    • 复用现存的类,接口与复用环境不一致时
    • 应对新环境的迁移,制定成新的一套新的接口规范即适配器
    1. 解决问题
      复用现有接口,解决接口不兼容的问题

    2. 举例
      在俄罗斯提供的插座是使用双脚圆形充电,而自带的充电器是双脚扁型
      那么可以在双脚扁型的充电器外面加一个电源适配器匹配俄罗斯的双脚圆形插座

    3. 分类

    • 类适配器
      多继承
      通过 private、protected、public 继承 IAdaptee 获得实现继承的效果
      通过 public 继承 ITarget 获得接口继承的效果
    class Adpater : public ITarget,
                    protected IAdaptee{
                      
                    }
    
    • 对象适配器
      对象组合
    class Adpater : public ITarget{
      protected:
        IAdaptee* pAdaptee;
    }
    
    1. UML
      适配器模式通用类图

    5.1.2. 使用效果?优缺点?

    优点:

    1. 提高了类的复用度,灵活性非常好

    缺点:
    1.

    5.1.3. 使用场景?业界知名例子

    适配器模式最好在详细设计阶段不要考虑它, 它不是为了解决还处在开发阶段的问题。

    这个模式使用的主要场景是扩展应用中, 系统扩展了但不符合原有设计的时候才考虑通过适配器模式减少代码修改带来的风险。

    5.2. 组合模式

    5.2.1. 简介

    1. 构造要点
    • 将对象组合成树形结构以表示“部分-整体”的层次结构(普遍存在的对象容器),组合模式使用户对单个对象和组合对象具有一致性(稳定)
    • 对于子节点(Leaf)的管理策略是STL中的vector,或数组、链表、Hash表等。
    • 组合模式核心是将客户代码和复杂的对象容器结构解耦,客户代码与纯粹的抽象接口发生依赖,而非对象容器的内部实现
    • 内部实现递归调用
    1. 解决问题
      只要是树形结构,体现局部和整体的关系时,考虑使用组合模式

    2. 举例
      江湖组织架构
      江湖公司(任我行)创建分支日月神教(东方不败)、五岳剑派(左冷蝉),
      还有叶子节点少林(方证大师)、武当(冲虚道长)

    3. UML
      组合模式通用类图

    叶子节点继承Component抽象基类不应该有Add()、Remove()、GetChild()方法,所以通用类图还有另一种表示方式:
    组合模式通用类图_Component没有Add等方法

    5.2.2. 使用效果?优缺点?

    优点:

    1. 高层模块调用简单
      一棵树形结构构中的所有节点都是Component, 局部和整体对调用者来说没有任何区别,也就是说, 高层模块不必关心自己处理的是单个对象还是整个组合结构, 简化了高层模块的代码。
    2. 节点自由扩展,增加树节点或叶子结点只要找到父节点即可,符合开闭原则。

    缺点:

    1. 树节点和叶节点使用时定义了直接使用的实现类!这在面向接口编程上是与依赖倒置原则冲突。

    5.2.3. 使用场景?业界知名例子

    1. 导航菜单一般都是树形的结构,文件夹管理等
    2. 常用的XML结构也是一个树形结构, 根节点、 元素节点、 值元素这些都与我们的组合模式相匹配

    5.3. 装饰者模式

    5.3.1. 简介

    1. 构造要点
    • 主体类在多个方向上的扩展功能
    • 装饰类继承主体类,也将主体具体类组合
    • 运行时动态扩展(多态)
    1. 解决问题
      动态地给对象添加一些额外的职责,就增减功能来说,装饰者比生成子类更灵活。

    2. 举例
      饮料为主体类,现在有黑咖啡和深度烘培咖啡豆两种,但现在我想让这些饮料有一些味道,怎么做?
      加调味品!奶油,摩卡,糖浆
      黑咖啡 + 奶油
      黑咖啡 + 摩卡

    3. UML
      装饰模式通用类图

    5.3.2. 使用效果?优缺点?

    优点:

    1. 扩展性非常好,装饰器模式比生成子类更灵活,可以动态扩展功能
    2. 装饰类和被装饰类可以独立发展, 而不会相互耦合。Decorator类是从外部来扩展Component类的功能, 而Decorator也不用知道具体的构件

    缺点:

    1. 有比较多的小对象,难维护,难排错。

    5.3.3. 使用场景?业界知名例子

    继承是静态地给类增加功能, 而装饰模式则是动态地增加功能。所以装饰模式可以替代继承, 解决我们类膨胀的问题。

    当需要动态地给一个对象增加功能,或动态地撤销功能,以及为一批的兄弟类进行改装或加装功能时,使用装饰模式。

    5.4. 桥接模式

    5.4.1. 简介

    1. 构造要点
    • 使用对象组合解耦了抽象与实现的关系
    • 子类要继承父类的关系,就必须先实现所有的方法,但是有很多方法子类不需要实现,此时可以将这些方法抽离出去,当子类在需要用时使用桥接模式去取这些方法
    • 继承关系多且复杂,颗粒度要求较细
    1. 解决问题
      将抽象部分和实现部分分离,使他们都可以独立地变化。桥接模式可以代替多继承方案。

    2. 举例
      将拉链式开关和电灯关联起来,将两位开关和风扇关联起来,这里主体是开关。
      开关是abstract,电器是implementor。可以看到的开关是抽象的,不用管里面具体怎么实现的。

    3. UML
      桥接模式通用类图

    5.4.2. 使用效果?优缺点?

    优点:

    1. 将抽象部分和实现部分分离,可以代替多继承方案。
    2. 易扩展,具有一定的封装性。实现细节对客户透明,它已经由抽象层通过聚合关系完成了封装。
      缺点:

    5.4.3. 使用场景?业界知名例子

    桥梁模式的意图还是对变化的封装, 尽量把可能变化的因素封装到最细、 最小的逻辑单元中, 避免风险扩散。 因此在进行系统设计时, 发现类的继承有N层时, 可以考虑使用桥梁模式。

    1. 不希望或不适用使用继承的场景
      例如继承层次过渡、 无法更细化设计颗粒等场景, 需要考虑使用桥梁模式。
    2. 接口或抽象类不稳定的场景
      明知道接口不稳定还想通过实现或继承来实现业务需求, 那是得不偿失的。
    3. 重用性要求较高的场景设计的颗粒度越细, 则被重用的可能性就越大, 而采用继承则受父类的限制, 不可能出
      现太细的颗粒度。

    5.5. 外观模式

    5.5.1. 简介

    1. 构造要点
    • 为子系统中的接口提供一致的界面,外观模式提供一个高层接口是的子系统更加容易使用
    • 外观模式更多的是架构设计模式,注重从架构模式去看整个系统,而不是单个类的层次(松耦合)
    • 组件内部应为耦合关系比较大的一系列组件,而不是任意的功能组合(高内聚)
      通常来说,只需要一个Facade对象,Facade对象一般为单例模式。
    1. 解决问题
      使外界通过子系统的高层接口访问系统内部实现,很好地实现隔离与封装

    2. 举例
      提供接口供其他人调用
      网购时流程:收到订单->提交给订单团队->提交给供应商->提交给快递员
      我们不需要知道其中的内部细节,如“收到订单->提交给订单团队”需要经历的步骤:“收到”, “确认付款”, “联系供应商”, “完成”

    3. UML
      外观模式通用类图

    解耦系统,隔离客户程序和系统。
    外观模式_接口隔离

    5.5.2. 使用效果?优缺点?

    优点:

    1. 减少系统的相互依赖。所有的依赖都是对Facade对象的依赖, 与子系统无关。不管子系统内部如何变化, 只要不影响到门面对象,任你自由活动。
    2. 对子系统来说更安全。
      缺点:
    3. 不符合开闭原则。 对修改关闭, 对扩展开放。这个缺点很严重。

    5.5.3. 使用场景?业界知名例子

    1. 为一个复杂的模块或子系统提供一个供外界访问的接口
    2. 子系统相对独立——外界对子系统的访问只要黑箱操作即可。
      比如银行利息的计算问题, 没有深厚的业务知识和扎实的技术水平是不可能开发出该子系统的, 但是对于使用该系统的开发人员来说, 他需要做的就是输入金额以及存期, 其他的都不用关心, 返回的结果就是利息。
    3. 预防低水平人员带来的风险扩散
      比如一个低水平的技术人员参与项目开发, 为降低个人代码质量对整体项目的影响风险, 一般的做法是“画地为牢”, 只能在指定的子系统中开发, 然后再提供Facade对象接口进行访问
      操作。

    5.6. 享元模式

    5.6.1. 简介

    1. 构造要点
    • 运用共享技术有效地支持大量细粒度的对象
    • 有一个享元对象工厂去获取对象
    • 对象由享元对象池管理,可以由map、list等实现
    • 对象池中已经有创建的对象就直接获取,没有对象就创建对象并插入对象池
    1. 解决问题
      解决面向对象的代价问题(性能问题)
      面对需要大量使用的对象时,采用对象共享的方法来降低系统中对象的个数
      降低细粒度对象给系统的压力

    2. 举例
      一个字母“ a”在文档中出现了100000 次, 而实际上我们可以让这一万个字母“ a” 共享一个对象,
      当然因为在不同的位置可能字母“ a” 有不同的显示效果(如字体和大小等设置不同)
      此时可将对象的状态分为“外部状态”和“ 内部状态”
      将可以被共享(不会变化)的状态作为内部状态存储在对象中
      而外部状态(如上面提到的字体、 大小等) 可作为参数传递给对象

    3. UML
      享元模式通用类图

    5.6.2. 使用效果?优缺点?

    优点:

    1. 大大减少应用程序创建的对象,减少程序内存,增强程序的性能。

    缺点:

    1. 提高了系统复杂性,需要分离出外部状态和内部状态。而且外部状态具有固化特性,不应该随内部状态改变而改变, 否则导致系统的逻辑混乱。
    2. 当对象池中的享元对象数量比想成数量还要少时,极可能发生线程不安全的问题。

    5.6.3. 使用场景?业界知名例子

    1. 系统中存在大量的相似对象。
    2. 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关。
    3. 需要缓冲池的场景。

    5.7. 代理模式

    5.7.1. 简介

    1. 构造要点
    • 为其他对象提供一种代理来控制对这个对象的访问
    • Proxy类知道需要代理的类的属性和方法
    • 在架构层次提供代理类,提供一层间接层,避免直接使用对象带来的一些问题
    1. 解决问题
      让抽象和实现彻底解耦

    2. 举例
      电信运行商提供充值服务,同时也有很多代理点提供充值服务

    3. UML
      代理模式通用类图

    4. 分类

    • 创建开销大的对象时候,比如显示一幅大的图片,我们将这个创建的过程交给代理去完成,GoF称之为虚代理(Virtual Proxy)。
    • 为网络上的对象创建一个局部的本地代理,比如要操作一个网络上的一个对象(网络性能不好的时候,问题尤其突出),我们将这个操纵的过程交给一个代理去完成,GoF称
      之为远程代理(Remote Proxy)。
    • 对对象进行控制访问的时候,比如在 Jive 论坛中不同权限的用户(如管理员、普通用户等)将获得不同层次的操作权限,我们将这个工作交给一个代理去完成,GoF称之为保护代理( Protection Proxy)。
    • 智能指针( Smart Pointer),取代了简单的指针,他在访问对象时执行一些附加的操作。关于这个方面的内容, 建议参看 Andrew Koenig 的《C++沉思录》中的第5章。

    5.7.2. 使用效果?优缺点?

    优点:

    1. 职责清晰,编程简洁清晰。真实的角色就是实现实际的业务逻辑, 不用关心其他非本职的事务, 通过后期的代理完成一件事务。
    2. 高扩展性,虽然具体主题角色是随时都会发生变化的, 但只要它实现了接口, 代理类完全可以在不做任何修改的情况下使用。
    3. 智能化,代理可分类,还有:动态代理(Java中的面向横切面编程,即AOP[AspectOriented Programming],其核心就是采用了动态代理机制)。动态代理指在实现阶段不用关心代理谁, 而在运行阶段才指定代理哪一个对象。

    缺点:
    1.

    5.7.3. 使用场景?业界知名例子

    代理模式应用得非常广泛,大到一个系统框架、企业平台, 小到代码片段、事务处理,都可能会用到代理模式。

    5.8. 结构型模式对比

    • 适配器模式 VS 代理模式
      适配器模式为他所是配的对象提供了一个不同的接口。
      代理模式提供了与代理类实体相同的接口,由于保护代理的存在,其接口实际上可能只是实体接口的一个子集。

    • 外观模式、代理模式、适配器模式、中介者模式都属于“接口隔离”原则的具体体现
      外观模式主要是针对系统外和系统内的接口隔离,解耦系统间的单向对象关联关系
      代理模式主要是针对两个对象,由于性能、安全、分布式的原因必须隔离,让A访问B的代理类
      适配器模式主要是解决接口不兼容的问题,复用老接口形成新接口
      中介者模式主要是解耦系统内各个对象之间的双向关联关系

    • 组合模式通过 VS 装饰者模式
      组合模式、装饰器模式UML类图相似,都基于递归组合来组织可变数目的对象。
      Composite 模式旨在构造类,使多个相关的对象能够以统一的方式处理,而多重对象可以被当作一个对象来处理。Composite 模式重在(结构)表示。
      Decorator 模式重在不生成子类即可给对象添加职责,Decorator 模式重在修饰。

    6. 行为型模式

    行为型模式用于管理对象之间的算法、关系和职责。主要有11种:

    • 模板方法模式(Template Method Pattern)
    • 策略模式(Strategy Pattern)
    • 观察者模式(Observer Pattern)
    • 命令模式(Command Pattern)
    • 迭代器模式(Iterator Pattern)
    • 中介者模式(Mediator Pattern)
    • 备忘录模式(Memento Pattern)
    • 解释器模式(Interpreter Pattern)
    • 状态模式(State Pattern)
    • 职责链模式(Chain of Responsibility Pattern)
    • 访问者模式(Visitor Pattern)

    6.1. 模板方法模式

    6.1.1. 简介

    1. 构造要点
    • 有一套固定的逻辑方法,写成抽象基类(虚函数可供扩展)
    • 继承抽象基类,子类实现细节(多态)
    1. 解决问题
      用于解决具有一定处理步骤的问题。架构师在获取需求后制定产品的大方向,制定一套逻辑框架,然后将任务分出去,提高工作效率。

    2. 举例
      校园招聘的流程:宣讲会->接受简历->面试->发放offer
      公司Company类为抽象基类,里面定义了校园招聘流程的每个流程方法
      其中面试和发放offer为虚函数,可供扩展
      这时有两个公司Alibaba和Tecent继承Company类实现上面两个虚函数,开启自己公司的校园招聘

    3. UML
      模板方法模式通用类图

    6.1.2. 使用效果?优缺点?

    优点:

    1. 封装不变部分, 扩展可变部分。模板方法模式通过把不变的行为搬移到超类,可变部分的则可以通过继承来继续扩展。基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。
    2. 提取公共部分代码, 便于维护。

    缺点:

    1. 每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。按照我们的设计习惯, 抽象类负责声明最抽象、 最一般的事物属性和方法, 实现类完成具体的事物属性和方法。 但是模板方法模式却颠倒了, 抽象类定义了部分抽象方法, 由子类实现, 子类执行的结果影响了父类的结果, 也就是子类对父类产生了影响, 这在复杂的项目中, 会带来代码阅读的难度, 而且也会让新手产生不适感。

    6.1.3. 使用场景?业界知名例子

    1. 多个子类有公有的方法, 并且逻辑基本相同时。
    2. 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
    3. 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。

    初级程序员在写程序的时候经常会问高手“父类怎么调用子类的方法”。那么父类是否可以调用子类的方法呢?
    能,但强烈地、极度地不建议这么做,那该怎么做呢?

    • 把子类传递到父类的有参构造中,然后调用。
    • 使用反射的方式调用,你使用了反射还有谁不能调用的!
    • 父类调用子类的静态方法。
      这三种都是父类直接调用子类的方法, 好用不? 好用! 解决问题了吗? 解决了! 项目中允许使用不? 不允许! 我就一直没有搞懂为什么要用父类调用子类的方法。 如果一定要调用子类, 那为什么要继承它呢? 搞不懂。 其实这个问题可以换个角度去理解, 父类建立框架,子类在重写了父类部分的方法后, 再调用从父类继承的方法, 产生不同的结果(而这正是模板方法模式) 。 这是不是也可以理解为父类调用了子类的方法呢? 你修改了子类, 影响了父类行为的结果, 曲线救国的方式实现了父类依赖子类的场景, 模板方法模式就是这种效果。模板方法在一些开源框架中应用非常多, 它提供了一个抽象类, 然后开源框架写了一堆子类。 如果你需要扩展功能, 可以继承这个抽象类, 然后覆写protected方法, 再然后就是调用一个类似execute方法, 就完成你的扩展开发, 非常容易扩展的一种模式。

    6.2. 策略模式

    6.2.1. 简介

    1. 构造要点
    • 各个步骤组装成算法 ,形成 策略抽象基类
    • 当不同的策略出现时可继承抽象基类,创建策略子类
    • 将算法的逻辑封装到一个类
    • 具体场景时选用不同的策略(组合对象,运行时可提供多态调用)
    1. 解决问题
      如果出现“if-else”“switch-case”等条件判断的情况,并且每个情况(算法)不变时,可考虑策略模式。

    2. 举例
      旅行时如何选择出行方式?自行车,小轿车,火车
      策略抽象基类IStrategy有虚函数Travel()
      BikeStrategy、CarStrategy、TrainStrategy继承抽象基类IStrategy,实现Travel()
      Context类封装逻辑可选择出行方式,含策略抽象基类IStrategy,真正实例化时可实例化子类
      这时你可以通过Context实例化对象,选择出行方式

    3. UML
      策略模式通用类图

    6.2.2. 使用效果?优缺点?

    优点:

    1. 算法可以自由切换。这是策略模式本身定义的, 只要实现抽象策略, 它就成为策略家族的一个成员, 通过封装角色对其进行封装, 保证对外提供“可自由切换”的策略。
    2. 避免使用多重条件判断。多重条件语句不易维护, 而且出错的概率大大增强。 使用策略模式后, 可以由其他模块决定采用何种策略, 策略家族对外提供的访问接口就是封装类, 简化了操作, 同时避免了条件语句判断。
    3. 扩展性良好,易于修改和扩展那些被复用的实现。 在现有的系统中增加一个策略太容易, 只要实现接口就可以了, 其他都不用修改, 类似于一个可反复拆卸的插件, 这大大地符合了开闭原则。

    缺点:

    1. 策略类数量增多。每一个策略都是一个类, 复用的可能性很小, 类数量增多,易引起类膨胀的问题。
    2. 所有的策略类都需要对外暴露上层模块必须知道有哪些策略, 然后才能决定使用哪一个策略, 这与迪米特法则是相违背的。如果系统中的一个策略家族的具体策略数量超过4个, 我们可以使用其他模式来修正这个缺陷,如工厂方法模式、 代理模式或享元模式, 解决策略类膨胀和对外暴露。

    6.2.3. 使用场景?业界知名例子

    1. 多个类只有在算法或行为上稍有不同的场景。
    2. 算法需要自由切换的场景。
      例如, 算法的选择是由使用者决定的, 或者算法始终在进化, 特别是一些站在技术前沿的行业, 连业务专家都无法给你保证这样的系统规则能够存在多长时间, 在这种情况下策略模式是你最好的助手。
    3. 需要屏蔽算法规则的场景。

    策略模式是一个非常简单的模式。 它在项目中使用得非常多, 但它单独使用的地方就比较少了。

    6.3. 观察者模式

    6.3.1. 简介

    1. 构造要点
    • 先构造观察者,有抽象基类,具体观察者类可以继承抽象基类
    • 管理观察者的类Subject,主要包含三个方法:注册观察者,注销观察者,通知观察者
    • 管理观察者的类Subject中用列表list<IObserver *>管理观察者
    1. 解决问题
      应对“一”对“多”的变化,当“一”变化时“多”也能随着改变自动刷新。

    2. 举例
      想要随时知道土豆的价格,需要派遣2位观察者Jack Ma、Pony随时观察土豆价格。
      先注册观察者,观察者发现土豆价格变化,自动通知刷新土豆价格。

    3. UML
      观察者模式通用类图

    6.3.2. 使用效果?优缺点?

    优点:

    1. 观察者和被观察者Subject之间是抽象耦合,即抽象基类是稳定的,同时也是可扩展的。
    2. 建立一套触发机制,形成了一个触发链。观察者模式可以完美地实现这种链条形式。

    缺点:

    1. 观察者模式需要考虑一下开发效率和运行效率问题,一个被观察者,多个观察者,开发和调试就会比较复杂。此处要考虑是否采用异步的方式。多级触发时的效率更是让人担忧。

    6.3.3. 使用场景?业界知名例子

    • 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
    • 事件多级触发场景。
    • 跨系统的消息交换场景,如消息队列的处理机制。

    注意事项:

    1. 广播链的问题
      如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表A上写了一个触发器,内容是一个字段更新后更新表B的一条数据,而表B上也有个触发器,要更新表C,表C也有触发器……完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题, 一个观察者可以有双重身份, 既是观察者,也是被观察者,这没什么问题呀但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者, 也就是说消息最多转发一次(传递两次),这还是比较好控制的。
      注意!!!它和职责链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而职责链模式在消息传递过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。
    2. 异步处理问题
      被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步,异步处理就要考虑线程安全和队列的问题,这个大家有时间看看Message Queue,就会有更深的了解。

    具体例子:

    1. MVC框架
      MVC框架中Model类担任目标角色,而View是观察者的基类。中介者模式中的同事类Colleage可以用观察者模式与中介者Mediator通讯。
    2. 文件系统
      比如,在一个目录下新建立一个文件,这个动作会同时通知目录管理器增加该目录,并通知磁盘管理器减少1KB的空间,也就说“文件”是一个被观察者,“目录管理器”和“磁盘管理器”则是观察者。
    3. 猫鼠游戏
      夜里猫叫一声,家里的老鼠撒腿就跑,同时也吵醒了熟睡的主人,这个场景中,“猫”就是被观察者,老鼠和人则是观察者。
    4. ATM取钱
      比如你到ATM机器上取钱,多次输错密码,卡就会被ATM吞掉,吞卡动作发生的时候,会触发哪些事件呢?第一,摄像头连续快拍,第二,通知监控系统,吞卡发生;第三,初始化ATM机屏幕,返回最初状态。一般前两个动作都是通过观察者模式来完成的,后一个动作是异常来完成。
    5. 广播收音机
      电台在广播,你可以打开一个收音机,或者两个收音机来收听,电台就是被观察者,收音机就是观察者。

    6.4. 中介者模式

    6.4.1. 简介

    1. 构造要点
    • 数据绑定,通讯协议
    • 将多个对象之间集中管理,变"多个对象互相关联"为"一个中介者和多个对象关联“
    • 解耦系统内各个对象之间的双向关联关系
    1. 解决问题
      解耦系统内各个对象之间的双向关联关系

    2. 举例
      房东通过中介者发送消息,中介者将消息发送给租客
      租客返回消息给中介者,中介者返回消息给房东
      中介者模式_例图

    3. UML
      中介者模式通用类图

    6.4.2. 使用效果?优缺点?

    优点:

    1. 减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,当然同时也降低了类间的耦合。

    缺点:

    1. 中介者会膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。

    6.4.3. 使用场景?业界知名例子

    中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构。在
    这种情况下一定要考虑使用中介者模式,这有利于把蜘蛛网梳理为星型结构,使原本复杂混乱的关系变得清晰简单。

    中介者模式很少用到接口或者抽象类,这与依赖倒置原则是冲突的,这是什么原因呢?
    首先,既然是同事类而不是兄弟类(有相同的血缘),那就说明这些类之间是协作关系,完成不同的任务,处理不同的业务,所以不能在抽象类或接口中严格定义同事类必须具有的方法。

    大家可以在如下的情况下尝试使用中介者模式:

    • N个对象之间产生了相互的依赖关系(N>2)。
    • 多个对象有依赖关系,但是依赖的行为尚不确定或者有发生改变的可能,在这种情况下一般建议采用中介者模式,降低变更引起的风险扩散。
    • 产品开发。 一个明显的例子就是MVC框架,中介者模式主要是应用于C(Controller),把中介者模式应用到产品中,可以提升产品的性能和扩展性,但是对于项目开发就未必,因为项目是以交付投产为目标,而产品则是以稳定、高效、扩展为宗旨。
    1. 机场调度中心
      大家在每个机场都会看到有一个“××机场调度中心”,它就是具体的中介者,用来调度每一架要降落和起飞的飞机。比如,某架飞机(同事类)飞到机场上空了,就询问调度中心(中介者)“我是否可以降落”以及“降落到哪个跑道”,调度中心(中介者)查看其他飞机(同事类)情况,然后通知飞机降落。如果没有机场调度中心,飞机飞到机场了,飞行员要先看看有没有飞机和自己一起降落的,有没有空跑道,停机位是否具备等情况,这种局面是难以想象的!
    2. MVC框架
      大家都应该使用过Struts,MVC框架。其中的C(Controller)就是一个中介者,叫做前端控制器(Front Controller),它的作用就是把M(Model,业务逻辑)和V(View,视图)隔离开,协调M和V协同工作,把M运行的结果和V代表的视图融合成一个前端可以展示的页面,减少M和V的依赖关系。MVC框架已经成为一个非常流行、成熟的开发框架。
    3. 媒体网关
      媒体网关也是一个典型的中介者模式,比如使用MSN时,张三发消息给李四,其过程应该是这样的:张三发送消息,MSN服务器(中介者)接收到消息,查找李四,把消息发送到李四,同时通知张三消息已经发送。在这里,MSN服务器就是一个中转站,负责协调两个客户端的信息交流,与此相反的就是IPMsg(也叫飞鸽),它没有使用中介者,而直接使用了UDP广播的方式,每个客户端既是客户端也是服务器端。
    4. 中介服务
      现在中介服务非常多,比如租房中介、出国中介。

    6.5. 状态模式

    6.5.1. 简介

    1. 构造要点
    • 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
    • 行为随着状态改变,在行为受状态约束的情况下可以使用状态模式, 但使用时对象的状态最好不
      要超过5个
    • 可以用状态模式实现的,一般也可以用策略模式实现。
    1. 解决问题
      行为随状态改变而改变的场景,条件、分支判断语句的替代者

    2. 举例
      电梯的动作:开门、关门、运行、停止
      可以通过设置电梯的状态,随之改变电梯的行为

    3. UML
      状态模式通用类图

    6.5.2. 使用效果?优缺点?

    优点:

    1. 结构清晰,避免了过多的switch…case或者if…else语句的使用, 避免了程序的复杂性,提高系统的可维护性。
    2. 很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,你只修改一个子类就可以了。
    3. 封装性非常好。这也是状态模式的基本要求,状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。

    缺点:

    1. 子类会太多,易引起类膨胀问题。如果一个事物有很多个状态也不稀奇,如果完全使用状态模式就会有太多的子类,不好管理,这个需要在项目中自己衡量。其实有很多方式可以解决这个状态问题,如在数据库中建立一个状态表,然后根据状态执行相应的操作,这个也不复杂。

    6.5.3. 使用场景?业界知名例子

    状态间的自由切换,那会有很多种呀!比如上面那个电梯的例子,我要一个正常的电梯运行逻辑,规则是开门->关门->运行->停止;还要一个紧急状态(如火灾)下的运行逻辑,关门->停止,紧急状态时,电梯当然不能用了;再要一个维修状态下的运行逻辑。需要我们把已经有的几种状态按照一定的顺序再重新组装一下,使用建造者模式!建造者模式+状态模式会起到非常好的封装作用。

    工作流开发, 应该有个状态机管理,如一个Activity(节点)有初始化状态(Initialized State)、挂起状态(Suspended State)、完成状态(Completed State)等,流程实例也有这么多状态, 那这些状态怎么管理呢?通过状态机(State Machine)来管理。

    使用场景:

    1. 行为随状态改变而改变的场景
      这也是状态模式的根本出发点,例如权限设计,人员的状态不同即使执行相同的行为结果也会不同,在这种情况下需要考虑使用状态模式。
    2. 条件、分支判断语句的替代者
      在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题, 它通过扩展子类实现了条件的判断处理。

    6.6. 备忘录模式

    6.6.1. 简介

    1. 构造要点
    • 在不破坏封装性的前提下,铺捉一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到该对象原先保存的状态
    • Memento备忘录对象在需要的时候恢复Originator发起者对象的状态
    • Memento核心是信息隐藏,Originator发起者对象需要向外界隐藏信息,保持其封装性,又需要将状态保持到外界
    1. 举例
      “月光宝盒”, 可以让我们回到需要的年代,返回上一个状态,即“撤销”的操作

    2. UML
      备忘录模式通用类图

    6.6.2. 使用效果?优缺点?

    优点:

    1. 封装性较高

    缺点:

    1. 使用备忘录代价较高,频繁地创建备忘录和恢复Originator状态,可能会导致非常大的开销。备忘录有可能有多个状态,管理器管理变复杂,也会需要大的存储开销。

    6.6.3. 使用场景?业界知名例子

    使用过程需要注意:

    • 备忘录的生命期
      备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。
    • 备忘录的性能
      不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的,系统的性能需要考虑。
    1. 需要保存和恢复数据的相关状态场景。
    2. 提供一个可回滚(rollback)的操作;比如Word中的CTRL+Z组合键,IE浏览器中的后退按钮,文件管理器上的backspace键等。
    3. 需要监控的副本场景中。例如要监控一个对象的属性,但是监控又不应该作为系统的主业务来调用,它只是边缘应用,即使出现监控不准、错误报警也影响不大,因此一般的做法是备份一个主线程中的对象,然后由分析程序来分析。
    4. 数据库连接的事务管理就是用的备忘录模式,JDBC驱动实现事务过程中使用备忘录模式。

    6.7. 迭代器模式

    6.7.1. 简介

    1. 构造要点
    • 提供一种方法顺序访问问一个聚合对象中的各个元素,而又不暴露该对象的内部结构表示(稳定)
    • 其他语言还是使用面向对象的方法实现迭代器(运行时多态),c++使用泛型编程模板方法实现迭代器(编译时多态效率更高),而不是运行时多态(c++抛弃)
    • 游标Cursor
    1. 举例
      一般库和框架都实现了迭代器模式,可以与组合模式使用

    2. UML
      迭代器模式通用类图

    3. 分类
      根据STL中的分类,iterator包括:
      输入迭代器(Input Iterator):通过对输入迭代器解除引用,它将引用对象,而对象可能位于集合中。最严格的输入迭代只能以只读方式访问对象。例如:istream。
      输出迭代器(Output Iterator):该类迭代器和Input Iterator极其相似,也只能单步向前迭代元素,不同的是该类迭代器对元素只有写的权力。例如:ostream, inserter。

    以上两种基本迭代器可进一步分为三类:
    ++、 --&&++、-n&+n
    前向迭代器(Forward Iterator):该类迭代器可以在一个正确的区间中进行读写操作,它拥有Input Iterator的所有特性,和Output Iterator的部分特性,以及单步向前迭代元素的能力。
    双向迭代器(Bidirectional Iterator):该类迭代器是在Forward Iterator的基础上提供了单步向后迭代元素的能力。例如:list, set, multiset, map, multimap。
    随机迭代器(Random Access Iterator):该类迭代器能完成上面所有迭代器的工作,它自己独有的特性就是可以像指针那样进行算术计算,而不是仅仅只有单步向前或向后迭代。例如:vector, deque, string, array。

    6.7.2. 使用效果?优缺点?

    优点:

    1. 在同一个聚合上支持以不同方式遍历,也可有多个遍历。
    2. 迭代器简化了聚合的接口。

    缺点:
    1.

    6.7.3. 使用场景?业界知名例子

    一般库和框架都实现了迭代器模式,可以与组合模式使用
    迭代器模式已经很少使用

    6.8. 职责链模式

    6.8.1. 简介

    1. 构造要点
    • 使多个对象都有机会处理请求, 从而避免了请求的发送者和接受者之间的耦合关系。 将这些对象连成一条链, 并沿着这条链传递该请求, 直到有对象处理它为止。
    • 如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制
    • 一个请求可能有多个接受者,但最后真正的接受者只有一个
    1. 解决问题
      解决提交帮助请求的对象(如按钮)并不明确知道谁是最终提供帮助的对象。职责链将提交帮助请求的对象和提供帮助的对象解耦。

    2. 举例
      职责链:经理->总监->ceo
      员工请假需请求上级批准
      Manager 可以批准 1 天假
      Director 可以批准 2 天假
      CEO 可以批准 7 天假

    3. UML
      职责链模式通用类图

    6.8.2. 使用效果?优缺点?

    优点:

    1. 将请求和处理分开。 请求者可以不用知道是谁处理的,处理者可以不用知道请求者的全貌。两者解耦,提高系统的灵活性。

    缺点:

    1. 性能问题,每个请求都是从链头遍历到链尾,特别
      是在链比较长的时候。
    2. 调试不很方便,特别是链比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。

    6.8.3. 使用场景?业界知名例子

    链中节点数量需要控制, 避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,然后判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能。

    许多类库使用职责链处理用户事件:当用户点击鼠标或按键盘时,一个事件产生并按链传播。

    6.9. 命令模式

    6.9.1. 简介

    1. 构造要点
    • 将一个请求封装成一个对象, 从而让你使用不同的请求把客户端参数化, 对请求排队或者记录请求日志, 可以提供命令的撤销和恢复功能
    • 有时必须向对象提交请求,但并不知道关于被请求的操作和请求的接受者的任何信息
    1. 举例
      用户界面工具箱包括按钮和菜单对象,他们执行请求响应用户输入。但工具箱不能显示地在按钮或菜单中实现该请求,因为只有使用工具箱的应用知道该由哪个对象做哪个操作,而工具箱的设计者无法知道请求的接受者或执行的操作。

    当有多个命令时,可以用组合模式封装成一个复合命令,如宏命令。

    1. UML
      命令模式通用类图

    6.9.2. 使用效果?优缺点?

    优点:

    1. 类间解耦
      调用者角色与接收者角色之间没有任何依赖关系, 调用者实现功能时只需调用Command抽象类的execute方法就可以, 不需要了解到底是哪个接收者执行。
    2. 可扩展性
      Command的子类可以非常容易地扩展, 而调用者Invoker和高层次的模块Client不产生严重的代码耦合。
    3. 命令模式结合其他模式会更优秀
      命令模式可以结合责任链模式, 实现命令族解析任务; 结合模板方法模式, 则可以减少Command子类的膨胀问题。

    缺点:

    1. 看Command的子类:如果有N个命令,这个类膨胀得非常大,需在项目中慎重考虑使用。

    6.9.3. 使用场景?业界知名例子

    只要你认为是命令的地方就可以采用命令模式, 例如, 在GUI开发中, 一个按钮的点击是一个命令, 可以采用命令模式; 模拟DOS命令的时候, 当然也要采用命令模式; 触发-反馈机制的处理等。

    6.10. 访问器模式

    6.10.1. 简介

    1. 构造要点
    • 不改变类层次结构,在运行时透明地为类层次结构上的各个类动态添加新的操作
    • 前提条件要求elemment抽象基类和其子类都是稳定的,这样visitor才可以访问每一个具体子类,动态添加新的操作
    • elemment类层次结构必须稳定,其中的操作可以面临频繁变动
    1. 举例
      西安有景点:钟楼、兵马俑
      到景点的人:游客、清洁工
      object_structure.h中的City对象提供访问方法绑定景点

    2. UML
      访问器模式通用类图

    6.10.2. 使用效果?优缺点?

    优点:

    1. 符合单一职责原则。具体元素角色也就是Element抽象类的两个子类负责数据的加载,而Visitor类则负责报表的展现,两个不同的职责非常明确地分离开来,各自演绎变化。
    2. 优秀的扩展性,由于职责分开, 继续增加对数据的操作(访问者)是非常快捷的。

    缺点:

    1. Visitor稳定的前提要求elemment抽象基类和其子类都是稳定的。具体元素对访问者公布细节,访问者要访问一个类就必然要求这个类公布一些方法和数据, 也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的。
    2. 具体元素变更比较困难,具体元素角色的增加、 删除、 修改都是比较困难的。
    3. 违背了依赖倒置转原则,访问者依赖的是具体元素,而不是抽象元素,这破坏了依赖倒置原则, 特别是在面向对象的编程中,抛弃了对接口的依赖,而直接依赖实现类,扩展比较难。

    6.10.3. 使用场景?业界知名例子

    1. 一个对象结构包含很多类对象, 它们有不同的接口, 而你想对这些对象实施一些依赖
      于其具体类的操作, 也就说是用迭代器模式已经不能胜任的情景。
    2. 需要对一个对象结构中的对象进行很多不同并且不相关的操作, 而你想避免让这些操
      作“污染”这些对象的类。

    总结一下,在这种地方你一定要考虑使用访问者模式:业务规则要求遍历多个不同的对象。这本身也是访问者模式出发点,迭代器模式只能访问同类或同接口的数据(当然了,如果你使用instanceof,那么能访问所有的数据,这没有争论),而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,然后执行不同的操作,也就是针对访问的对象不同,执行不同的操作。访问者模式还有一个用途,就是充当拦截器(Interceptor)角色。

    访问者模式是一种集中规整模式,特别适用于大规模重构的项目,在这一个阶段需求已经非常清晰,原系统的功能点也已经明确,通过访问者模式可以很容易把一些功能进行梳理,达到最终目的——功能集中化,如一个统一的报表运算、 UI展现等,我们还可以与其他模式混编建立一套自己的过滤器或者拦截器。

    6.11. 解析器模式

    6.11.1. 简介

    1. 构造要点
    • 给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。
    • 解析器模式中使用类来表示文法规则,因此可以使用面向对象的方法实现文法的扩展。另外对于终结符我们可以使用享元模式来实现终结符的共享。
    • 但只适用于比较简单的文法表示,复杂的还需要求助语法分析生成器等工具。
    1. 举例
      解析器下的四则运算
      构建语法规则:变量表达式,符号表达式,加法运算,减法运算

    2. UML
      解析器模式通用类图

    6.11.2. 使用效果?优缺点?

    优点:

    1. 解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以,若扩展语法,则只要增加非终结符类就可以。

    缺点:

    1. 解释器模式会引起类膨胀
      每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。
    2. 解释器模式采用递归调用方法
      每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个断点一个断点地调试下去,直到最小的语法单元。
    3. 效率问题
      解释器模式由于使用了大量的循环和递归,效率是一个不容忽视的问题,特别是一用于解析复杂、冗长的语法时,效率是难以忍受的。

    6.11.3. 使用场景?业界知名例子

    解释器模式在实际的系统开发中使用得非常少,因为它会引起效率、 性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,如一些数据分析工具、 报表设计工具、科学计算工具等。

    若你确实遇到“一种特定类型的问题发生的频率足够高”的情况, 准备使用解释器模式时, 可以考虑一下Expression4J、 MESP(Math Expression String Parser) 、 Jep等开源的解析工具包(这三个开源产品都可以通过百度、 Google搜索到),功能都异常强大, 而且非常容易使用, 效率也还不错, 实现大多数的数学运算完全没有问题, 自己没有必要从头开始编写解释器。

    尽量不要在重要的模块中使用解释器模式, 否则维护会是一个很大的问题。 在项目中可以使用shell、 JRuby、 Groovy等脚本语言来代替解释器模式, 弥补编译型语言的不足。

    另外,在学习编译器原理课程时,将语言从汇编语言->可执行文件时也会用到解析器模式,使用场景还有:

    1. 重复发生的问题可以使用解释器模式
      例如,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素是相同的,按照解释器的说法就是终结符表达式都是相同的,但是非终结符表达式就需要制定了。在这种情况下,可以通过程序来一劳永逸地解决该问题。
    2. 一个简单语法需要解释的场景
      为什么是简单?看看非终结表达式,文法规则越多,复杂度越高,而且类间还要进行递归调用。排查问题越难。因此,解释器模式一般用来解析比较标准的字符集,例如SQL语法分析,不过该部分逐渐被专用工具所取代。

    6.12. 行为型模式对比

    • 模板方法模式、策略模式、观察者模式相似

    • 状态模式 VS 策略模式
      两者最大的差别就是 State 模式中派生类持有指向 Context 对象的依赖,并通过这个依赖调用、 Context 中的方法,但在 Strategy 模式中就没有这种情况。因此可以说一个 State 实例同样是 Strategy 模式的一个实例,反之却不成立。

    7. 参考资料

    1. CSDN博客 c++设计模式 https://blog.csdn.net/weixin_35602748/article/details/78667543
    2. GitHub源码地址 Waleon/DesignPatterns https://github.com/Waleon/DesignPatterns
    3. 《设计模式之禅》 秦小波著 ISBN: 9787111437871
    4. c++设计模式 视频教程 李建忠讲师 https://www.bilibili.com/video/av52251106
    5. CSDN博客 设计模式思维导图 https://blog.csdn.net/weixin_35602748/article/details/78667543
    6. 《GoF23种设计模式模式解析附C++实现源码》
      起效率、 性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,如一些数据分析工具、 报表设计工具、科学计算工具等。

    若你确实遇到“一种特定类型的问题发生的频率足够高”的情况, 准备使用解释器模式时, 可以考虑一下Expression4J、 MESP(Math Expression String Parser) 、 Jep等开源的解析工具包(这三个开源产品都可以通过百度、 Google搜索到),功能都异常强大, 而且非常容易使用, 效率也还不错, 实现大多数的数学运算完全没有问题, 自己没有必要从头开始编写解释器。

    尽量不要在重要的模块中使用解释器模式, 否则维护会是一个很大的问题。 在项目中可以使用shell、 JRuby、 Groovy等脚本语言来代替解释器模式, 弥补编译型语言的不足。

    另外,在学习编译器原理课程时,将语言从汇编语言->可执行文件时也会用到解析器模式,使用场景还有:

    1. 重复发生的问题可以使用解释器模式
      例如,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素是相同的,按照解释器的说法就是终结符表达式都是相同的,但是非终结符表达式就需要制定了。在这种情况下,可以通过程序来一劳永逸地解决该问题。
    2. 一个简单语法需要解释的场景
      为什么是简单?看看非终结表达式,文法规则越多,复杂度越高,而且类间还要进行递归调用。排查问题越难。因此,解释器模式一般用来解析比较标准的字符集,例如SQL语法分析,不过该部分逐渐被专用工具所取代。

    6.12. 行为型模式对比

    • 模板方法模式、策略模式、观察者模式相似

    • 状态模式 VS 策略模式
      两者最大的差别就是 State 模式中派生类持有指向 Context 对象的依赖,并通过这个依赖调用、 Context 中的方法,但在 Strategy 模式中就没有这种情况。因此可以说一个 State 实例同样是 Strategy 模式的一个实例,反之却不成立。

    7. 参考资料

    1. CSDN博客 c++设计模式 https://blog.csdn.net/weixin_35602748/article/details/78667543
    2. GitHub源码地址 Waleon/DesignPatterns https://github.com/Waleon/DesignPatterns
    3. 《设计模式之禅》 秦小波著 ISBN: 9787111437871
    4. c++设计模式 视频教程 李建忠讲师 https://www.bilibili.com/video/av52251106
    5. CSDN博客 设计模式思维导图 https://blog.csdn.net/weixin_35602748/article/details/78667543
    6. 《GoF23种设计模式模式解析附C++实现源码》
    7. 《设计模式 可复用面向对象软件的基础》
    展开全文
    qq_34183232 2021-01-29 09:49:33
  • 主要介绍七大原则23种设计模式 原则:1、单一原则2、开闭原则3、里氏替换原则4、接口隔离原则5、依赖倒置原则6、迪米特法则7、合成复用原则 设计模式:1、单例模式2、工厂模式3、抽象工厂模式4、原型模式5、建造者...

    一、前言

    Java设计模式在平时的开发中起到了至关重要的作用。设计模式的主要目的是:
    1、降低耦合度,使得类的修改不至于“牵一发而动全身”。
    2、提高可扩展性,新增功能对原有的代码没什么影响
    3、提高可复用性,降低过多的使用类时,导致类爆炸的情况
    4、提高灵活性,代码能够通过接口灵活调用
    5、提高可维护性,修改的地方越少

    二、七大原则

    1、单一原则:一个类只负责一个职责
    2、开闭原则:对修改关闭,对扩展开放
    3、里氏替换原则:不要破坏继承关系
    4、接口隔离原则:暴露最小接口,避免接口过于臃肿
    5、依赖倒置原则:面向抽象编程
    6、迪米特 法则:尽量不跟陌生人讲话
    7、合成复用原则:多使用组合、聚合、少用继承

    1、单一原则

    一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序,实现 高内聚,低耦合

    优点:降低耦合,提高可维护性,对一个类进行单独处理并且不影响其他类

    2、开闭原则

    对修改关闭,对扩展开放,核心是:抽象化,多态使用
    优点:提高可扩展性,提高可维护性,提高可复用性

    3、里氏替换原则

    所有使用父类的地方,必须能够透明的使用子类对象

    例如:父类Animal;子类Dog继承父类Animal,子类Cat继承父类Animal。

    Animal animal=new Dog();
    //同样可以透明的使用
    Animal animal=new Cat();
    

    当子类重写父类的方法时,则不适合使用里氏替换原则。

    4、接口隔离原则

    每一个接口承担独立的角色,只干自己该干的事情。
    只暴露最小接口,实现类不需要用到实现的方法不要放在接口。

    5、依赖倒置原则

    指的是面向抽象编程,对抽象类或接口进行依赖,比较灵活。
    比如:有抽象类Animal,子类Dog继承父类Animal,子类Cat继承父类Animal。在依赖的时候用抽象类,在使用的时候指定子类

    //抽象类和子类
    abstract class  Animal{
    	abstract void hello();
    }
    class Dog extends Animal{
    	@Override
    	void hello() {
    		System.out.println("hello ,I am dog");
    	}
    }
    class Cat extends Animal{
    	@Override
    	void hello() {
    		System.out.println("hello ,I am cat");	
    	}
    }	
    
    //依赖到抽象类
    class Client{
    Animal a;
    	public Client(Animal a){
    		this.a=a;
    	}
    	public void hello(){
    		a.hello();
    	}
    }
    //测试类进行使用
    public class Test {
    
        public static void main(String args[]) {
       //在使用到Animal的使用,需要定义它的子类
       	Client c=new Client(new Dog());
        c.hello();//打印指定的方法
        }
    }
    

    6、迪米特法则

    尽量不要跟陌生人说话。
    不是陌生人的情况:

    • 当前对象本身(this)
    • 方法参数
    • 对象的成员对象
    • 对象所创建的对象

    目的:降低耦合,高内聚

    7、合成复用原则

    尽量使用组合、聚合的方式,少使用继承。
    原因:继承会导致耦合度变高。
    聚合方式的使用案例:

    class A{}	
    class Test{
    A a;
    	public void useA(A a){
    		this.a=a;
    	}
    }
    

    组合方式的使用案例:

    class A{}	
    class Test{
    	A a=new A();
    }
    

    组合和聚合的区别 : 组合方式在使用Test对象的时候会立即开辟A对象的空间,而聚合的方式是在使用到A对象的时候才会开辟A对象的空间。

    三、23种设计模式

    23种设计模式
    创建型模式:主要关注点是“怎样创建对象”

    1、单例模式(Singleton)

    特点:当需求是一个类只需要一个实例
    单例模式有:1、饿汉模式;2、懒汉模式;3、双重检查模式;4、枚举模式

    饿汉模式(推荐使用)

    特点: 当类加载时创建好对象,并且在外部不能通过new创建该对象,只能调用这个类的的方法进行调用。线程安全,在实际开发中用的最多
    案例:
    方式一

    /**
     * 饿汉方式一
     * @Author:小庄
     */
    public class Singleton {
        //private不允许外部调用new创建对象
        private Singleton(){}
        //使用静态关键字”static“保证只有一次
        private static Singleton instance=new Singleton();
        //外部可直接通过类进行调用静态方法
        public static Singleton getInstance(){
            return instance;
        }
    }
    /**
     * 饿汉方式二
     * @Author:小庄
     */
    class Singleton2 {
        private static Singleton2 instance;
        private Singleton2(){
            instance=new Singleton2();
        }
    /**
     * 饿汉方式二
     * @Author:小庄
     */
    class Singleton2 {
        private static Singleton2 instance;
        static {
            instance=new Singleton2();
        }
        private Singleton2(){}
        //外部可直接通过类进行调用静态方法
        public static Singleton2 getInstance(){
            return instance;
        }
    }
    

    懒汉模式(不推荐使用)

    特点: 等需要该类的时候再加载,会考虑线程安全问题
    1、不安全的懒汉模式

    /**
     * 线程不安全的懒汉模式
     * @Author:小庄
     */
    class Singleton{
        private static Singleton instance;
        private Singleton(){}
        public static Singleton getInstance(){
        	instance=new Singleton();
            return instance;
        }
    }
    class Singleton2{
        private static Singleton2 instance;
        private Singleton2(){}
        public static  Singleton2 getInstance(){
        	if(instance==null){
    	    	synchronized(Singleton2.class){
    	    		instance=new Singleton2();
    	    	}	
        	}
            return instance;
        }
    }
    

    以上代码线程不安全的原因:当几个线程同时访问的时候,就有可能创建多个对象实例。

    2、线程安全的懒汉模式

    /**
     * 线程安全的懒汉模式
     * @Author:小庄
     */
    class Singleton{
        private static Singleton instance;
        private Singleton(){}
        public static synchronized Singleton getInstance(){
        	if(instance==null){
        		instance=new Singleton();
        	}
            return instance;
        }
    }
    

    以上代码缺点:当getInstance()方法里面的逻辑代码很复杂时,所有的代码都被加锁,会大大的消耗性能

    双重检查懒汉模式(线程安全)

    /**
     * 线程安全的懒汉模式
     * @Author:小庄
     */
    class Singleton{
        private static Singleton instance;
        private Singleton(){}
        public static  Singleton getInstance(){
        	if(instance==null){
        		//进行加锁
        		synchronized(Singleton.class){
        		//再进行一次判断类是否为空
    				if(instance==null){
    					instance=new Singleton();
    				}
    			}
        	}
            return instance;
        }
    }
    

    静态内部类实现单例

    /**
     *内部类实现单例
     * @Author:小庄
     */
    class Singleton{
        //设置为private
        private Singleton(){}
        //内部类的特性,外部类不能直接访问
        private static class GetSingleton{
           private static final Singleton instance=new Singleton();
        }
        public Singleton getInstance(){
            return GetSingleton.instance;
        }
    }
    

    运用枚举实现单例(推荐使用)

    enum Singleton2{
        ONE,TWO,THREE;//一个属性代表一个实例,这里有三个实例
    }
    

    小结

    推荐使用饿汉模式和枚举模式实现单例的原因是线程安全,而且简单明了
    不推荐使用懒汉模式的原因是:1、部分的懒汉模式不安全 ;2、过于繁琐 ;3、有可能会对性能造成额外消耗

    2、工厂模式(Factoy)

    特点:对类的创建用一个工厂类进行管理。

    工厂模式分为:简单工厂,工厂方法和抽象工厂,这节内容主要讲简单工厂和工厂方法。

    简单工厂

    由一个工厂对象决定创建出哪一种产品类的实例
    代替构造函数创建对象,方法名比构造函数清晰。
    简单工厂的类图:

    简单工厂
    具体代码实现:

    public class SimpleFactory{
    	public createProduct(int i){
    		switch (i){
                case 0: new Product1();break;
                case 1: new Product2();break;
                case 2: new Product3();break;
                default: System.out.println("没有该产品");
            }
    	}
    	//具体的使用者
    	public static void main(String[] args){
    		SimpleFactory factory=new SimpleFactory();
    		//通过工厂进行创建类
    		factory.createProduct(1);
    	}
    }
    class Product1{}
    class Product2{}
    class Product3{}
    
    

    工厂方法

    工厂方法

    //抽象类
    abstract class FactoryMethod {
    		//生产方法
    		public abstract void production(String productName);
    }
    class Product1 extends FactoryMethod {
    	//实现抽象方法
    	@Override
    	public void production(String productName) {
    		System.out.println("Product1生产了"+productName+"产品");
    		
    	}
    }
    class Product2 extends FactoryMethod {
    	//实现抽象方法
    	@Override
    	public void production(String productName) {
    		System.out.println("Product2生产了"+productName+"产品");
    		
    	}
    }
    //使用者
    public class Client{
    	public static void main(String[] args) {
    		FactoryMethod factory=new Product1();
    		factory.production("x");
    	}
    }
    

    3、抽象工厂模式(AbstractFactory)

    创建一组有关联的对象实例

    角色:

    • AbstractProduct (抽象产品)
    • AbstractFactory (抽象工厂)
    • Client (委托者)

    抽象工厂
    具体实现如下,为了保证代码的阅读,这里不对类的具体操作进行展开

    //定义抽象工厂类
    abstract class AbstractFactory {
    	//定义抽象方法,返回值是抽象产品类对象
    	abstract AbstractClass1 createClass1();
    	abstract AbstractClass2 createClass2();
    	abstract AbstractClass3 createClass3();
    }
    //把抽象产品类全部定义完
    abstract class AbstractClass1 {}
    abstract class AbstractClass2 {}
    abstract class AbstractClass3 {}
    //定义具体产品类,分别继承对应的抽象产品类
    class Class1 extends AbstractClass1{}
    class Class2 extends AbstractClass2{}
    class Class3 extends AbstractClass3{}
    //定义具体工厂类,实现抽象方法,具体工厂类1
    class Factory1 extends AbstractFactory{
    	@Override
    	AbstractClass1 createClass1() {
    		// TODO Auto-generated method stub
    		return new Class1();
    	}
    	@Override
    	AbstractClass2 createClass2() {
    		// TODO Auto-generated method stub
    		return new Class2();
    	}
    	@Override
    	AbstractClass3 createClass3() {
    		// TODO Auto-generated method stub
    		return new Class3();
    	}
    }
    //具体工厂类2
    class Factory2 extends AbstractFactory{
    	@Override
    	AbstractClass1 createClass1() {
    		// TODO Auto-generated method stub
    		return new Class1();
    	}
    	@Override
    	AbstractClass2 createClass2() {
    		// TODO Auto-generated method stub
    		return new Class2();
    	}
    	@Override
    	AbstractClass3 createClass3() {
    		// TODO Auto-generated method stub
    		return new Class3();
    	}
    }
    

    分析

    从上面代码中,我们可以发现,抽象工厂处理的是产品一族,它们和工厂方法的区别在于:工厂方法有利于处理一个产品的部件扩展维度,而抽象工厂有利于扩展产品一族维度。

    4、原型模式(Prototype)

    特点:必须实现Cloneable接口,并且重写clone方法,否则会报错
    应用场景:一个对象属性特别多,同时指定很麻烦,实际工作中用的很少

    角色:

    • Prototype(原型)
    • ConcretePrototype(具体原型)
    • Client(使用者)

    浅克隆

    //原型接口继承Cloneable接口,或者抽象类实现Cloneable接口
    interface Prototype extends Cloneable{
    }
    //具体原型类实现原型接口,重写方法
    public class ConcretePrototype implements Prototype {
        @Override
        protected Object clone() throws CloneNotSupportedException {
           //通过克隆自身对象
            ConcretePrototype p=(ConcretePrototype)this.clone();
            //返回克隆后的值
            return p;
        }
    }
    

    通过clone实现深拷贝

    //原型接口继承Cloneable接口,或者抽象类实现Cloneable接口
    interface Prototype extends Cloneable{
    }
    //具体原型类实现原型接口,重写方法
    public class ConcretePrototype implements Prototype {
        //假设有一个对象属性,需要克隆这个对象属性
        public Book book;
        @Override
        protected Object clone() throws CloneNotSupportedException {
            //对基本属性进行克隆
            Object deep=null;
            deep=super.clone();
            //对引用类型的数据进行单独处理
            ConcretePrototype c=(ConcretePrototype )deep;
            c.book= (Book)book.clone();
            //返回克隆后的值
            return deep;
        }
    }
    

    缺点:繁琐
    通过序列化实现深拷贝

    public class Book implements Serializable{
      
        //浅克隆,使用默认的clone方法
        public Object deepClone() {
    
            ByteArrayInputStream bis=null;
            ByteArrayOutputStream bos=null;
            ObjectOutputStream oos=null;
            ObjectInputStream ois=null;
            try{
                bos=new ByteArrayOutputStream();
                oos=new ObjectOutputStream(bos);
                oos.writeObject(this);
                //反序列化
                bis=new ByteArrayInputStream(bos.toByteArray());
                ois=new ObjectInputStream(bis);
                 Book book=(Book)ois.readObject();
                return book;
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
                return null;
            }finally {
                try {
                    bos.close();
                    oos.close();
                    bis.close();
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    5、建造者模式(Builder)

    特点:处理复杂对象的创建,在使用时只需要用想要的属性时,不需要设置所有参数;这个建造者模式和模板方法特别像

    角色:

    • Builder(建造者)
    • ConcreteBuilder(具体建造者)
    • Director(监工)
    • Client(使用者)
    //建造者接口或抽象类
    interface Builder {
    	//具体步骤
    	Builder creteBuilder();
    	Builder creteBuilder2();
    	Builder creteBuilder3();
    }
    //具体建造类
    class ConcreteBuilder implements Builder{
    	//实现具体步骤,返回值为当前对象,便于链式编程
    	@Override
    	public Builder creteBuilder() {
    		System.out.println("建造第一层");
    		return this;
    	}
    	@Override
    	public Builder creteBuilder2() {
    		System.out.println("建造第二层");
    		return this;
    	}
    	@Override
    	public Builder creteBuilder3() {
    		System.out.println("建造第三层");
    		return this;
    	}
    }
    //监工
    class Director {
    	//将抽象类(接口)聚合,然后对抽象方法进行调用,会指向子类实现的方法
    	Builder builder;
    	public Director(Builder builder) {
    		this.builder=builder;
    	}
    	public void toBuilder() {
    		builder.creteBuilder().creteBuilder2().creteBuilder3();
    	}
    }
    //使用者
    public class Client {
    	public static void main(String[] args) {
    		//通过监工直接建造
    		Director d=new Director(new ConcreteBuilder());
    		d.toBuilder();
    	}
    }
    

    比较常用的使用方式

    //模拟建造者模式的具体使用
    public class Person {
    	//这里定义了很多的属性,但是有时候只需要部分的属性
    	int id;
    	String name;
    	int age;
    	double weight;
    	int score;
    	//将构造函数设置为私有属性,不让外部进行访问
    	private Person() {}
    	//定义内部类,通过内部类对类的成员变量进行操作,可以叫它为静态工厂
    	public static class PersonBuilder{
    		Person p=new Person();
    		//每次方法返回值不为void,而是直接返回当前的对象,有利于链式方程
    		public PersonBuilder basicInfo(int id,String name,int age) {
    			p.id=id;
    			p.name=name;
    			p.age=age;
    			return this;
    		}
    		public PersonBuilder weight(double weight) {
    			p.weight=weight;
    			return this;
    		}
    		public PersonBuilder score(int score) {
    			p.score=score;
    			return this;
    		}
    		//通过下面方法返回外部类对象
    		public Person build() {return p;}
    	}
    	//打印
    	public static void main(String[] args) {
    		//采用链式编程,直观
    		Person p=new Person.PersonBuilder().basicInfo(1, "张三", 18).weight(65.0).build();
    	}
    }
    

    6、适配器模式(Adapte)

    使不兼容的接口相融

    角色:

    • Target(对象)
    • Adaptee(被适配)
    • Adapter(适配)

    1、类适配器模式
    类适配器

    //目标接口
    interface Target {
    	public int open110V();
    }
    //被适配类
    class ClassAdaptee{
    	public int open220V(){
    		return 200;
    	}
    }
    //适配类
    public class ClassAdapte extends ClassAdapteeimplements Target{
    	@Override
    	public int open110V() {
    		int voltage=open220V();
    		return voltage/2;
    	}
    }
    

    2、对象适配器

    对象适配器
    和类适配器的区别,适配器和被适配者之间没有继承关系,通过聚合实现(合成复用)

    //目标类,可以用抽象类代替
    abstract Target {
    	public abstract int open110V();
    }
    //被适配类
    class ClassAdaptee{
    	public int open220V(){
    		return 200;
    	}
    }
    //适配类
    public class ClassAdapte extends Target{
    	public ClassAdaptee adaptee;
    	public ClassAdapte(ClassAdaptee e){
    		this.adaptee=e;
    	}
    	@Override
    	public int open110V() {
    		int voltage=e.open220V();
    		return voltage/2;
    	}
    }
    

    7、桥连模式(Bridge)了解

    特点:
    双维度扩展
    分离抽象和具体
    用聚合的方式连接抽象和具体

    类图
    在这里插入图片描述

    abstract class AbstractBridge {
    	//聚合实现者
    	Implementor imt;
    }
    //具体桥连者
    class ConcreteBride extends AbstractBridge{
    	//把实现者作为参数
    	public ConcreteBride(Implementor imt) {
    		this.imt=imt;
    	}
    }
    //抽象化实现者
    interface Implementor {
    }
    //具体实现者
    public class ConcreteImplementor implements Implementor{
    }
    

    8、装饰者模式(Decorator)

    特点:对类进行扩展时不修改原有的代码,为类添加新的功能,防止类继承带来的爆炸性增长

    类图:
    装饰者模式

    案例应用:
    装饰者模式

    //定义Drink抽象类,并设定好抽象方法
     abstract class Drink {
    	private String des;//描述
    	private float price=0.0f;//价格
    	public String getDes() {
    		return des+"价格:"+price;
    	}
    	public void setDes(String des) {
    		this.des = des;
    	}
    	public float getPrice() {
    		return price;
    	}
    	public void setPrice(float price) {
    		this.price = price;
    	}
    	//计算费用的抽象方法
    	protected abstract float cost();
    }
    
    //Coffee类继承Drink抽象类,因为Coffee只是一种饮料
    abstract class Coffee extends Drink{
    }
    //定义具体咖啡,并设置好属性
    class Cafe_Latte extends Coffee{
    
    	public Cafe_Latte() {
    		super.setDes("拿铁咖啡");
    		super.setPrice(18.80f);
    	}
    	@Override
    	protected float cost() {
    		//获取价格
    		return super.getPrice();
    	}
    }
    //咖啡的具体实现类
    class Instant_Coffee extends Coffee{
    	
    	public Instant_Coffee(int shuliang) {
    		super.setDes("速溶咖啡");
    		super.setPrice(14.00f);
    	}
    	@Override
    	protected float cost() {
    		return super.getPrice();
    	}
    }
    class Cafe_Latte extends Coffee{
    
    	public Cafe_Latte() {
    		super.setDes("拿铁咖啡");
    		super.setPrice(18.80f);
    	}
    	@Override
    	protected float cost() {
    		return super.getPrice();
    	}
    }
    //通过继承Drink方便进行装饰类
     class DeCorator extends Drink{
    	private Drink drink;
    	private String des;//描述
    	private float price=0.0f;//价格
    	private int shuliang=1;//数量
    	public DeCorator(Drink drink) {
    		this.drink=drink;
    	}
    	public DeCorator(Drink drink,int shuliang) {
    		this.drink=drink;
    		this.shuliang=shuliang;
    	}
    	public String getDes() {
    		return drink.getDes()+" "+" 加入"+shuliang+"份"+des+" "+des+"单价:"+price;
    	}
    	public void setDes(String des) {
    		this.des = des;
    	}
    	public float getPrice() {
    		return price;
    	}
    	public void setPrice(float price) {
    		this.price = price;
    	}
    	protected float cost() {
    		float material_price=this.price*this.shuliang;
    		return material_price+drink.cost();
    	}
    }
    //装饰者的具体实现类
    class Milk extends DeCorator{
    	public Milk(Drink drink) {
    		super(drink);
    		super.setDes("牛奶");
    		super.setPrice(2f);
    	}
    	public Milk(Drink drink, int shuliang) {
    		super(drink, shuliang);
    		super.setDes("牛奶");
    		super.setPrice(2f);
    	}
    }
    //装饰者的具体实现类
    class Sugar extends DeCorator{
    	public Sugar(Drink drink) {
    		super(drink);
    		super.setDes("糖");
    		super.setPrice(1f);
    	}
    	public Sugar(Drink drink, int shuliang) {
    		super(drink, shuliang);
    		super.setDes("糖");
    		super.setPrice(1f);
    	}
    }
    //使用者测试类
    public class Client {
    	public static void main(String[] args) {
    		Drink order=new Cafe_Latte();
    		System.out.println("费用:"+order.cost());
    		System.out.println(order.getDes());
    		order=new Milk(order);
    		System.out.println("加入一份牛奶的费用:"+order.cost());
    		System.out.println(order.getDes());
    	}
    }
    
    

    9、享元模式(Flyweight)

    特点:共享数据,重复利用对象

    应用场景:数据库连接池

    类图:
    享元模式
    角色:

    • 抽象化享元对象
    • 享元对象
    • 享元池

    下面是图书馆案例,图书馆作为享元池,书架作为抽象化享元对象,书籍作为享元对象。

    来看类图
    享元模式

    //抽象化享元对象
    abstract class Book {
    	HashMap<String,Boolean> start=new HashMap<>();
    	abstract void borrow(Lender lender);
    	abstract void setBookStart(String bookName,boolean bookStart);
    	abstract boolean getBookStart(String bookName);
    }
    //具体享元对象
    class MixBook extends Book{
    	private String bookName="";//书名
    	private boolean bookStart;
    	public MixBook(String bookName) {
    		this.bookName=bookName;
    	}
    	@Override
    	public void borrow(Lender lender) {
    		System.out.println(lender.getName()+"向图书馆借出一本"+bookName);
    	}
    	public String getBookName() {
    		return bookName;
    	}
    	public void setBookName(String bookName) {
    		this.bookName = bookName;
    	}
    	public boolean getBookStart(String bookName) {
    		if(start.get(bookName)) return true;
    		else return false;
    	}
    	public void setBookStart(String bookName,boolean bookStart) {
    		start.put(bookName, bookStart);
    	}
    }
    //外部状态
    class Lender {
    	private String name;
    	public Lender(String name) {
    		this.name = name;
    		
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getName() {
    		return name;
    	}
    }
    import java.util.HashMap;
    //享元池
    class Library {
    	//设置图书馆为单例,只能生成一个实例对象
    	private static Library library=new Library();
    	//防止外部通过new生成实例
    	private Library(){};
    	//书架
    	HashMap<String,Book> pool=new HashMap<>();
    	
    	public static Library getInstance() {
    		return library;
    	}
    	public Book toBorrow(String bookName,Lender lender) {
    		Book book=null;
    		if(pool.isEmpty()) {
    			System.out.println("图书馆目前没有书,请添加书籍");
    		}
    		else if(!pool.containsKey(bookName)) {
    			//如果没有,提示图书馆没有这本书,请联系图书管理员
    			System.out.println("图书馆没有"+bookName+",请联系图书管理员");
    			
    		}else {
    			book=pool.get(bookName);
    			if(!book.getBookStart(bookName)){
    				System.out.println("你好"+lender.getName()+":"+bookName+"已经被借走了");
    				book=null;
    			}
    			else {
    				book.start.put(bookName, false);
    				book.borrow(lender);
    			}
    		}
    		return book;
    	}
    	public void shelveBook(String bookName,Book book) {
    		book.setBookStart(bookName,true);
    		pool.put(bookName, book);
    	}
    	public int poolSize() {
    		return pool.size();
    	}
    }
    //使用享元池
    public class Student {
    	public static void main(String[] args) {
    		//通过单例模式进行创建图书馆享元池
    		Library library=Library.getInstance();
    		library.shelveBook("《Java编程思想》", new MixBook("《Java编程思想》"));
    		library.shelveBook("《Mysql必会知识》", new MixBook("《Mysql必会知识》"));
    		//设置借阅人
    		Lender lender=new Lender("小庄");
    		Book book1=library.toBorrow("《Java编程思想》",lender);
    		Book book2=library.toBorrow("《Java编程思想》",lender);
    		Book book3=library.toBorrow("《Mysql必会知识》",lender);
    		//查看享元池借出的书籍
    		System.out.println("图书馆借出书籍共:"+library.poolSize()+"个");
    	}
    }
    

    10、代理模式(Proxy)

    作用:在程序运行期间,在不修改源代码的情况下对方法进行功能增强

    优势:减少重复代码,提高开发效率,并且便于维护

    底层实现:在运行期间,通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

    代理模式分为:静态代理,动态代理
    本篇内容的动态代理只对jdk代理和cglib代理介绍

    静态代理

    静态代理
    角色:

    • 代理类
    • 被代理类
    • 抽象化被代理类
    • 使用者
    //抽象化被代理类
    interface IBoard {
    	void draw();
    }
    //具体被代理类
    class Board implements IBoard{
    	@Override
    	public void draw() {
    		System.out.println("被代理类画画操作执行");
    	}
    }
    //静态代理类
    class StaticProxy implements IBoard{
    	//聚合抽象化被代理类
    	private IBoard board;
    	public StaticProxy(IBoard board) {
    		this.board=board;
    	}
    	//通过代理类执行被代理类的方法
    	@Override
    	public void draw() {
    		System.out.println("代理类执行自己操作");
    		System.out.print("============");
    		board.draw();//调用被代理类执行的方法
    		System.out.print("============");
    		System.out.println("代理类执行结束");
    	}
    }
    //使用者
    public class Client {
    	public static void main(String[] args) {
    		//通过静态代理类进行调用被代理类的方法
    		new StaticProxy(new Board()).draw();
    	}
    }
    

    动态代理模式之JDK代理

    JDK代理
    jdk代理主要通过反射的方式进行代理,JDK代理本身就继承了Proxy类,由于Java不支持多继承,所以不支持对类的代理,只支持对接口的代理。
    具体案例如下:

    //接口1
    interface IBoard {
    	void draw();
    }
    //接口2
    interface IComputer {
    	void open();
    	void complete();
    }
    //接口1的实现类
    class Board implements IBoard{
    	@Override
    	public void draw() {
    		System.out.println("被代理类画画操作执行");
    	}
    }
    //接口2的实现类
    class Computer implements IComputer{
    	@Override
    	public void open() {
    		System.out.println("电脑正在启动....");	
    	}
    	@Override
    	public void complete() {
    		System.out.println("电脑启动完成");
    	}
    }
    //代理类
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    class JDKProxy {
    	//设置被代理对象
    	private Object target;
    	//通过构造函数设置被代理对象
    	public JDKProxy(Object target) {
    	 this.target=target;
    	}
    	//被代理对象生成
    	public Object getProxyInstance() {
    		//返回一个JDK代理类对象
    		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {
    
    			@Override
    			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    				System.out.println("=================");
    				System.out.println("代理开始");
    				Object object=method.invoke(target, args);
    				System.out.println("被代理的方法名:"+method.getName());
    				if("open"==method.getName()) {
    					Method complete=target.getClass().getMethod("complete");
    					if(null!=complete){
    						System.out.println("complete方法被代理对象自动调用");
    						Thread.sleep(1000);
    						complete.invoke(target, args);
    					}
    				}
    				System.out.println("代理结束");
    				System.out.println("=================");
    				System.out.println();
    				return object;
    			}
    		});
    	}
    }
    //使用者
    public class Client {
    	public static void main(String[] args) {
    		//代理画板对象
    		JDKProxy board=new JDKProxy(new Board());
    		IBoard iBoard=(IBoard)board.getProxyInstance();
    		//调用画板的draw()方法
    		iBoard.draw();
    		//代理电脑对象
    		JDKProxy computer=new JDKProxy(new Computer());
    		IComputer iComputer=(IComputer) computer.getProxyInstance();
    		//这里只调用了open()方法,但是complete会被代理对象自动调用
    		iComputer.open();
    	}
    }
    

    动态代理之cglib代理

    Cgilb代理
    我们知道,使用Jdk代理的不足之处是不能对类进行代理,而Cglib代理刚好解决了这个问题。
    Cglib可以对无接口的类进行代理,需要实现MethodInterceptor接口

    具体案例:

    //没有实现接口的类1
    class Board{
    	public void draw() {
    		System.out.println("被代理类画画操作执行");
    	}
    }
    //没有实现接口的类2
    class Computer{
    	public void open() {
    		System.out.println("电脑正在启动....");
    	}
    	public void complete() {
    		System.out.println("电脑启动完成");	
    	}
    }
    //Cglib代理
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    //实现MethodInterceptor接口,重写intercept方法(实现代理功能)
    class CglibProxy implements MethodInterceptor{
    	//设置被代理类
    	public Object target;
    	//通过构造函数进行设置
    	public CglibProxy(Object target) {
    		this.target=target;
    	}
    	/**
    	*实现代理的关键:
    	*1.创建工具类Enhancer
    	*2.设置它的父类,会在虚拟机生成一个父类
    	*3.设置回调函数
    	*4.创建子类对象,作为代理对象
    	*/
    	public Object getProxyInstance() {
    		//创建工具类
    		Enhancer e=new Enhancer();
    		//设置父类
    		e.setSuperclass(target.getClass());
    		//设置回调函数
    		e.setCallback(this);
    		//创建子类对象,作为代理对象
    		return e.create();
    	}
    	@Override
    	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
    		System.out.println("===============");
    		//代理模式底层使用反射
    		System.out.println("Cglib代理模式开始");
    		//invoke激活,和jdk代理功能类似
    		Object object=method.invoke(target, args);
    		System.out.println("被代理的方法名:"+method.getName());
    		//在代理类实现自己的功能
    		if("open"==method.getName()) {
    			Method complete=target.getClass().getMethod("complete");
    			if(null!=complete){
    				System.out.println("complete方法被代理对象自动调用");
    				Thread.sleep(1000);
    				complete.invoke(target, args);
    			}
    		}
    		System.out.println("代理结束");
    		System.out.println("=================");
    		System.out.println();
    		return object;
    	}
    }
    

    另一种方式:不需要类去实现MethodInterceptor接口,是在回调函数的实现这个接口

    //没有实现接口的类1
    class Board{
    	public void draw() {
    		System.out.println("被代理类画画操作执行");
    	}
    }
    //没有实现接口的类2
    class Computer{
    	public void open() {
    		System.out.println("电脑正在启动....");
    	}
    	public void complete() {
    		System.out.println("电脑启动完成");	
    	}
    }
    //Cglib代理
    import java.lang.reflect.Method;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    //实现MethodInterceptor接口,重写intercept方法(实现代理功能)
    class CglibProxy2{
    	//设置被代理类
    	public Object target;
    	//通过构造函数进行设置
    	public CglibProxy2(Object target) {
    		this.target=target;
    	}
    	/**
    	*实现代理的关键:
    	*1.创建工具类Enhancer
    	*2.设置它的父类,会在虚拟机生成一个父类
    	*3.设置回调函数
    	*4.创建子类对象,作为代理对象
    	*/
    	public Object getProxyInstance() {
    		//创建工具类
    		Enhancer e=new Enhancer();
    		//设置父类
    		e.setSuperclass(target.getClass());
    		//设置回调函数
    		e.setCallback(new MethodInterceptor() {
    
    			@Override
    			public Object intercept(Object arg0, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    				System.out.println("===============");
    				//代理模式底层使用反射
    				System.out.println("Cglib代理模式开始");
    				//invoke激活,和jdk代理功能类似
    				Object object=method.invoke(target, args);
    				System.out.println("被代理的方法名:"+method.getName());
    				//在代理类实现自己的功能
    				if("open"==method.getName()) {
    					Method complete=target.getClass().getMethod("complete");
    					if(null!=complete){
    						System.out.println("complete方法被代理对象自动调用");
    						Thread.sleep(1000);
    						complete.invoke(target, args);
    					}
    				}
    				System.out.println("代理结束");
    				System.out.println("=================");
    				System.out.println();
    				return object;
    			}
    		});
    		//创建子类对象,作为代理对象
    		return e.create();
    	}
    }
    

    11、组合模式(Composite)

    一般适用于树状结构的场景,比如文件结构,比如层级结构
    组合模式

    实现代码如下:

    //结点
    abstract class Node {
    	public abstract void printNode();
    }
    //导入相关的包
    import java.util.ArrayList;
    import java.util.List;
    //根结点/子结点
    class BranchNode extends Node{
    	//给结点起个名字
    	String nodeName;
    	//将它以下结点进行收集
    	public List<Node> nodes=new ArrayList<>();
    	public BranchNode(String nodeName) {
    		this.nodeName=nodeName;
    	}
    	public void addNode(Node node) {
    		nodes.add(node);
    	}
    	//打印结点
    	@Override
    	public void printNode() {
    		System.out.println(nodeName);
    	}
    }
    //叶子结点
    class LeafNode extends Node{
    	//给结点起个名字
    	String nodeName;
    	public LeafNode(String nodeName) {
    		this.nodeName=nodeName;
    	}
    	//打印叶子结点信息
    	@Override
    	public void printNode() {
    		System.out.println(nodeName);
    	}
    }
    //使用者
    public class Client {
    	public static void main(String[] args) {
    		BranchNode root=new BranchNode("根结点");
    		BranchNode node1=new BranchNode("结点1");
    		Node c1=new LeafNode("叶子结点1");
    		Node c2=new LeafNode("叶子结点2");
    		//添加结点
    		root.addNode(node1);
    		//添加叶子结点
    		node1.addNode(c1);
    		node1.addNode(c2);
    		//使用递归遍历	
    		tree(root,0);
    	}
    	public static void tree(Node node,int depth) {
    		for(int i=0;i<depth;i++) {
    			System.out.print("--");
    		}
    		node.printNode();
    		//判断是否是根结点
    		if(node instanceof BranchNode) {
    			//递归遍历结点
    			for(Node n:((BranchNode) node).nodes){
    				tree(n,depth+1);
    			}
    		}
    	}
    }
    

    生活中用到组合模式的例子:学校,学院(系)和专业
    组合模式的应用

    //结点
    abstract class Composite {
    	private String des;
    	private String name;
    	public Composite(String name,String des) {
    		this.name=name;
    		this.des=des;
    	}
    	protected void add(Composite composite) {
    	}
    	protected void remove(Composite composite) {}
    	protected abstract void print();
    	public String getDes() {
    		return des;
    	}
    	public void setDes(String des) {
    		this.des = des;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    }
    //学院(系)
    import java.util.HashSet;
    
    class College extends Composite{
    	HashSet<Major> majors=new HashSet<>();
    	public College(String name,String des) {
    		super(name,des);
    	}
    	@Override
    	protected void add(Composite composite) {
    		majors.add((Major)composite);
    	}
    	@Override
    	protected void remove(Composite composite) {
    		majors.remove((Major)composite);
    	}
    	@Override
    	public String getName() {
    		return super.getName();
    	}
    	@Override
    	public String getDes() {
    		return super.getDes();
    	}
    	@Override
    	protected void print() {
    		System.out.println("学院:"+getName());
    		System.out.println();
    		for(Major major:majors) {
    		System.out.println("专业:"+major.getName()+"\t专业详情:"+major.getDes());
    		}
    		System.out.println("=====================");
    	}
    }
    //导入相关包
    
    import java.util.Set;
    //学校
    class University extends Composite{
    	//学校由多个学院(系)组成
    	Set<College> colleges=new HashSet<>();
    	
    	public University(String name,String des) {
    		super(name,des);
    	}
    	@Override
    	protected void add(Composite composite) {
    		colleges.add((College)composite);
    	}
    	@Override
    	protected void remove(Composite composite) {
    		colleges.remove((College)composite);
    	}
    	@Override
    	public String getName() {
    		return super.getName();
    	}
    	@Override
    	public String getDes() {
    		return super.getDes();
    	}
    	@Override
    	protected void print() {
    		System.out.println("========"+getName()+"========");
    		System.out.println("简介:"+getDes());
    		System.out.println();
    		for(College college:colleges) {
    		System.out.println("学院:"+college.getName()+"\t学院详情:"+college.getDes());
    		}
    		System.out.println("=====================");
    	}
    }
    //专业,对于叶子结点
    public class Major extends Composite{
    	public Major(String name,String des) {
    		super(name,des);
    	}
    	@Override
    	protected void print() {
    		System.out.println(super.getName()+super.getDes());
    	}
    }
    //使用者
    public class Client {
    public static void main(String[] args) {
    	University university=new University("清华大学", "中国顶尖学府");
    	College c=new College("计算机学院","培养软件+硬件高级人才");
    	university.add(c);
    	university.add(new College("金融学院", "培养金融行业高级人才"));
    	university.print();
    	c.add(new Major("计算机科学与技术","老牌的计算机专业"));
    	c.print();
    	}
    }
    

    12、外观模式(Facade)

    外观模式又称为门面模式,通常和责任链模式结合使用
    使用场景:解决多个类之间的复杂关系,只需要通过一个类就可以直接和其他类进行交互,具体使用:中间件

    通过案例进行讲解:
    外观模式

    //经纪人,门面
    class Agent {
    	private static Agent agent=new Agent();
    	//通过单例模式获取类对象
    	protected Star star=Star.getInstance();;
    	protected Fans fans=Fans.getInstance();
    	protected Company company=Company.getInstance();
    	private Agent() {}
    	//见面机会方法
    	protected void meeting() {
    		System.out.println("粉丝"+fans.getName()+"与"+star.getName()+"获得一次见面机会");
    	}
    	//签约方法
    	protected void contract() {
    		System.out.println("明星"+star.getName()+"与"+company.getName()+"进行了签约");
    	}
    	public static Agent getInstance() {
    		return agent;
    	}
    }
    //明星类
    class Star {
    	private static Star star=new Star();
    	protected volatile String name="";
    	private Star() {}
    	public String getName() {
    		return name;
    	}
    	public synchronized void setName(String name) {
    		this.name = name;
    	}
    	protected static Star getInstance() {
    		return star;
    	}
    }
    //粉丝
    class Fans {
    	private static Fans fans=new Fans();
    	private volatile String name="";
    	private Fans() {}
    	public String getName() {
    		return name;
    	}
    
    	public synchronized void setName(String name) {
    		this.name = name;
    	}
    	public static Fans getInstance() {
    		return fans;
    	}
    }
    //签约公司
    class Company {
    	private volatile String  name="";
    	private static Company company=new Company();
    	private Company() {}
    	public String getName() {
    		return name;
    	}
    	public synchronized void setName(String name) {
    		this.name = name;
    	}
    	public static Company getInstance() {
    		return  company;
    	}
    }
    //用户测试
    public class Client {
    	public static void main(String[] args) {
    		//直接跟经纪人打交道
    		Agent agent=Agent.getInstance();
    		agent.company.setName("公司x");
    		agent.fans.setName("张三");
    		agent.star.setName("吴签");
    		agent.meeting();
    		agent.contract();
    		System.out.println("============");
    		agent.fans.setName("李四");
    		agent.meeting();
    		agent.contract();
    	}
    }
    

    我们可以看到,经纪人就是一个门面,其他人都是通过经纪人来处理事情。粉丝想要见明星,首先要通过经纪人。再由经纪人和明星打交道。

    13、状态模式(State)

    如果一个类有很多个复杂状态的时候,可以把状态抽象出来,具体状态实现这个抽象类,

    角色:

    • State(状态)
    • ConcreteState(具体状态)
    • Context(状况)
    //设置抽象化状态类
    abstract class State {
    	public abstract void simle();
    	public abstract void cry();
    }
    //开心状态
    class HappyState extends State{
    	@Override
    	public void simle() {
    		System.out.println("开心的笑");
    	}
    	@Override
    	public void cry() {
    		System.out.println("开心的哭了");
    	}
    }
    //不开心状态
    class SadState extends State{
    	@Override
    	public void simle() {
    		System.out.println("沮丧的笑着");
    	}
    	@Override
    	public void cry() {
    		System.out.println("笑哭了");
    	}
    }
    //状况
    public class Context{
    	//聚合状态
    	private State state;
    	//设置状态
    	public Context(State state) {
    		this.state=state;
    	}
    	//根据状态判断
    	public void simle() {
    		state.simle();
    	}
    	//根据状态判断
    	public void cry() {
    		state.cry();
    	}
    }
    

    我们不难发现,抽象类的状态(抽象方法)是固定的,具体是怎样的状态,由子类实现。如果抽象类的状态(抽象方法)变化的很频繁,会对所有子类产生影响,那么就不建议使用这个模式

    14、责任链模式(Responsibility)

    是一种链式的结构的模式,通常使用在审批流,过滤器

    责任链模式

    //设置责任链的抽象类(接口)
    abstract class Approve {
    	private Approve approve;
    	//设置下一个审批人
    	public void setApprove(Approve approve) {
    		this.approve=approve;
    	}
    	//审批人的具体审批内容
    	public abstract void toApprove();
    	public abstract void chain(Approve approve);
    }
    //责任链的内容
    //审批人1
    class Approve1 extends Approve{
    	//写入审批人的审批内容
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人1,我审批完之后交给审批人2");
    		//设置它的下一个审批人
    		chain(new Approve2());
    	}
    	//设置它的下一个审批人
    	@Override
    	public void chain(Approve approve) {
    		approve.toApprove();
    	}
    }
    class Approve2 extends Approve {
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人2,我审批完之后交给审批人3");
    		chain(new Approve3());
    	}
    	@Override
    	public void chain(Approve approve) {
    		approve.toApprove();
    	}
    }
    class Approve3 extends Approve {
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人3,审批完成");
    		
    	}
    	@Override
    	public void chain(Approve approve) {
    		approve.toApprove();
    	}
    }
    //请求访问责任链
    public class Request {
    	public static void main(String[] args) {
    		Approve approve=new Approve1();
    		approve.toApprove();
    	}
    }
    

    换一种思路,我们把每一个子类存到容器中,然后对容器进行遍历,然后执行方法,也类似这种链式的执行流程。
    我们来通过代码来看看

    abstract class Approve {
    	//审批人的具体审批内容
    	public abstract void toApprove();
    }
    //审批人1
    class Approve1 extends Approve{
    	//写入审批人的审批内容
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人1,我审批完之后交给审批人2");
    	}
    }
    //审批人2
    class Approve2 extends Approve {
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人2,我审批完之后交给审批人3");
    	}
    }
    //审批人3
    class Approve3 extends Approve {
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人3,审批完成");
    	}
    }
    //导入集合
    import java.util.ArrayList;
    //责任链管理类
    class ApproveChain {
    	//聚合了内容
    	ArrayList<Approve> approves=new ArrayList<>();
    	public ApproveChain addChain(Approve approve) {
    		approves.add(approve);
    		return this;
    	}
    	//通过调用这个方法,遍历容器中的类执行方法
    	public void chain() {
    		for(Approve a:approves) {
    			a.toApprove();
    		}
    	}
    }
    //请求着使用
    public class Request {
    	public static void main(String[] args) {
    		//定义责任链管理类
    		ApproveChain ac=new ApproveChain();
    		//需要设置它的下一个
    		ac.addChain(new Approve1()).addChain(new Approve2()).addChain(new Approve3());
    		ac.chain();
    	}
    }
    

    上面代码很类似享元模式吧,哈哈。

    15、迭代器模式(Iterator)

    角色:

    • Iterator(迭代器)
    • ConcreteIterator(具体的迭代器)
    • Aggregate(集合)
    • CocreteAggreagete(具体集合)

    类图:
    迭代器模式
    Iterator(迭代器)

    public interface Iterator_ {
    	boolean hashNext();
    	Object next();
    }
    

    ConcreteIterator(具体的迭代器)

    public class ArrayListIterator implements Iterator_{
    	//单例模式创建类
    	private static ArrayListAggregate alat=new ArrayListAggregate();
    	@Override
    	public boolean hashNext() {
    		if(alat.currentIndex>=alat.size()) return true;
    		return false;
    	}
    	@Override
    	public Object next() {
    		Object o=alat.list.get(alat.currentIndex);
    		alat.currentIndex++;
    		return o;
    	}
    }
    

    Aggregate(集合接口)

    public interface Aggregate {
    	public abstract Iterator_ iterator();
    }
    

    CocreteAggreagete(具体集合)

    import java.util.ArrayList;
    
    public class ArrayListAggregate implements Aggregate{
    	ArrayList<Object> list=new ArrayList<>();
    	int index=0;
    	int currentIndex=0;
    	@Override
    	public Iterator_ iterator() {
    		// TODO Auto-generated method stub
    		return new ArrayListIterator();
    	}
    	public int size() {
    		
    		return index;
    	}
    	public void add(Object o) {
    		list.add(o);
    		index++;
    	}
    }
    

    16、观察者模式(Observer)重要

    我们开发中,使用到观察者模式很多,这个知识点要重点把握

    通知对象状态改变,允许一个对象向所有侦听对象广播自己的消息或事件

    角色:

    • Source:事件源对象
    • Observer:观察者
    • Event:事件

    观察者

    //事件,可定义为抽象化类(接口)
    class Event {
    	String loc;
    	public Event(String loc) {
    		this.loc=loc;
    	}
    }
    //抽象化观察者
    abstract class Observer {
    	public abstract void actionEvent(Event event);
    }
    //具体观察者1
    class Observer1 extends Observer{
    	@Override
    	public void actionEvent(Event event) {
    		//打印
    		System.out.println(event.loc);
    		System.out.println("观察者1做出了行为");
    		//执行相关的方法
    	}
    }
    //具体观察者2
    class Observer2 extends Observer{
    	@Override
    	public void actionEvent(Event event) {
    		System.out.println("============");
    		System.out.println("观察者2做出了行为");
    		//执行相关的操作
    	}
    }
    
    import java.util.ArrayList;
    import java.util.List;
    //由事件源发出事件,观察者进行处理事件
    public class Source {
    	private List<Observer> observers=new ArrayList<>();
    	{	
    		//注册观察者
    		observers.add(new Observer1());
    		observers.add(new Observer2());
    	}
    	//执行这个方法会触发所有观察者
    	public void wakeUp() {
    		for(Observer o:observers) {
    			o.actionEvent(new Event("bed"));
    		}
    	}
    }
    //测试
    class Test {
    	public static void main(String[] args) {
    		new Source().wakeUp();
    	}
    }
    

    17、策略模式(Strategy)

    策略模式
    角色:

    • 抽象化策略
    • 具体策略
    • 聚合策略类
    • 使用者

    封装的是不同的执行方式,比如对排序选择不同的排序算法(策略)
    代码如下

    //抽象化策略类(接口)
    interface Sorter{
    	public abstract void sort(Comparable[] data);
    }
    //具体策略,这里采用选择排序算法
    class SelectSorter implements Sorter{
    	@Override
    	public void sort(Comparable[] data) {
    		for(int i=0;i<data.length-1;i++) {
    			int min=i;
    			for(int j=i+1;j<data.length;j++) {
    				if(data[min].compareTo(data[j])>0) {
    					min=j;
    				}
    			}
    			swap(data,i,min);
    		}
    	}
    	public void swap(Comparable[] data,int i,int min) {
    		Comparable temp=data[i];
    		data[i]=data[min];
    		data[min]=temp;
    	}
    }
    //具体策略类,这里采用冒泡排序
    class BubbleSorter implements Sorter{
    	@Override
    	public void sort(Comparable[] data) {
    		//进行排序
    		for(int i=1;i<data.length;i++) {
    			for(int j=0;j<data.length-i;j++) {
    				if(data[j].compareTo(data[j+1])>0) {
    					Comparable temp=data[j+1];
    					data[j+1]=data[j];
    					data[j]=temp;
    				}
    			}
    		}
    	}
    }
    //聚合策略类,可以选择不同的策略
    class SortAndPrint{
    	Comparable[] data;
    	Sorter sorter;
    	public SortAndPrint(Comparable[] data,Sorter sorter) {
    		this.data=data;
    		this.sorter=sorter;
    	}
    	public void sort() {
    		System.out.println("====排序前====");
    		print();
    		sorter.sort(data);
    		System.out.println();
    		System.out.println("====排序后====");
    		print();
    	}
    	public void print() {
    		for(int i=0;i<data.length;i++) {
    			System.out.print(data[i]+",");
    		}
    		System.out.println(" ");
    	}
    }
    //测试
    public class Client {
    
    	public static void main(String[] args) {
    		//定义一个需要排序的数据
    		String[] data= {"Dumpty","Bowman","Carroll"};
    		SortAndPrint sap=new SortAndPrint(data, new BubbleSorter());
    		sap.sort();
    	}
    }
    

    18、模板方法模式(Temlapte Method)

    具体实现交给子类
    角色:

    • AbstractClass
    • concreteClass

    类图:
    模板方法模式

    具体使用:

    public abstract class AbstractDisplay {
    	//定义抽象方法,由子类去实现
    	public abstract void open();
    	public abstract void print();
    	public abstract void close();
    	//对这个方法设定为final类型。表示子类不能重写
    	public final void display() {
    		open();
    		print();
    		close();
    	}
    	//使用
    	public static void main(String[] args){
    		CharDisplay chars=new CharDisplay();
    		chars.display();
    	}
    }
    //子类继承
    class CharDisplay extends AbstractDisplay{
    	@Override
    	public void open() {
    		System.out.println("打开CharDisplay");	
    	}
    	@Override
    	public void print() {
    		System.out.println("在CharDisplay进行打印");
    	}
    	@Override
    	public void close() {
    		System.out.println("关闭CharDisplay");
    	}
    }
    //子类继承
    class StringDisplay extends AbstractDisplay{
    
    	@Override
    	public void open() {
    		System.out.println("打开StringDisplay");	
    	}
    
    	@Override
    	public void print() {
    		System.out.println("在StringDisplay进行打印");
    	}
    
    	@Override
    	public void close() {
    		System.out.println("关闭StringDisplay");
    	}
     	public void printLine() {
    		System.out.println("StringDisplay自定义的方法");
    	}
    }
    

    应用场景:

    1. 算法的整体步骤很固定,但其中个别部分易变,将易变部分交给子类实现。
    2. 多个子类存在公共行为,可以将其提取出并集中到一个公共父类以避免代码重复
    3. 需要控制子类的扩展时,模板方法只在特定点调用钩子函数操作,这样就只允许在这些点进行扩展。

    19、中介者模式(Mediator)

    中介这个角色在生活中的理解是:比如租房者找中介,再由中介跟相关的房东沟通,房东也是通过中介和租房者沟通。中介是中间的桥梁。
    在中介者模式中的理解和生活中的理解基本一致

    角色:

    • Mediator 抽象化中介者
    • ConcentrateMediator 具体中介者
    • College 抽象化同事类
    • ConcentrateCollege 具体同事类(租房者、房东)

    中介者模式(小庄)

    //抽象化中介者
    pabstract class Mediator {
    	//沟通的方法
    	public abstract void constact(String msg,Colleague c);
    }
    //具体中介类
    class ConcreteMediator extends Mediator{
    	//聚合同事类
    	private ConcreteColleague1 c1;
    	private ConcreteColleague2 c2;
    	//设置get和set方法
    	public ConcreteColleague1 getC1() {
    		return c1;
    	}
    	public void setC1(ConcreteColleague1 c1) {
    		this.c1 = c1;
    	}
    	public ConcreteColleague2 getC2() {
    		return c2;
    	}
    	public void setC2(ConcreteColleague2 c2) {
    		this.c2 = c2;
    	}
    	//具体的沟通方法,核心
    	@Override
    	public void constact(String msg, Colleague c) {
    		//判断是哪个同事,进行处理事件
    		if(c==c1) {
    			c2.getMessage(msg);
    		}else {
    			c1.getMessage(msg);
    		}
    	}
    }
    //同事类
    abstract class Colleague {
    	//给同事类命名
    	protected String name;
    	//设置中介
    	protected Mediator mediator;
    	public Colleague(String name,Mediator mediator) {
    		this.name=name;
    		this.mediator=mediator;
    	}
    	//沟通的方法
    	public abstract void constact(String msg);
    	//获取信息
    	public abstract void getMessage(String msg);
    }
    //具体的同事类,这个类表示租房
    class ConcreteColleague1 extends Colleague{
    	
    	//在构造函数让该类被创建的时候同时命名
    	public ConcreteColleague1(String name,Mediator mediator) {
    		super(name, mediator);
    	}
    	@Override
    	public void constact(String msg) {
    		mediator.constact(msg, this);
    	}
    	@Override
    	public void getMessage(String msg) {
    		System.out.println();
    		System.out.println("租房者姓名:"+name+"。收到的信息:"+msg);
    	}
    }
    //具体的同事类,这里表示房东
    class ConcreteColleague2 extends Colleague{
    
    	//在构造函数让该类被创建的时候同时命名
    	public ConcreteColleague2(String name,Mediator mediator) {
    		super(name,mediator);
    	}
    	@Override
    	public void constact(String msg) {
    		mediator.constact(msg, this);
    	}
    	@Override
    	public void getMessage(String msg) {
    		System.out.println("房东姓名:"+name+"。收到的信息:"+msg);
    	}
    }
    //测试类
    public class Client {
    	public static void main(String[] args) {
    		//创建中介者
    		ConcreteMediator cm=new ConcreteMediator();
    		//创建同事类
    		ConcreteColleague1 c1=new ConcreteColleague1("张三",cm);
    		ConcreteColleague2 c2=new ConcreteColleague2("李四",cm);
    		//房东需要知道具体的租房者和房东
    		cm.setC1(c1);
    		cm.setC2(c2);
    		c1.constact("我想租两房一厅的房子");
    		c2.constact("我这有,而且非常便宜");
    	}
    }
    

    20、备忘录模式(Memento)

    记录状态,便于回滚,经常和命令模式结合使用
    备忘录模式(小庄)
    角色:

    • Memento 备忘录,记录状态
    • Originator 创建者:创建备忘录
    • Caretaker 管理者:管理备忘录
    //备忘录,需要存的内容
    class Memento {
    	private String state;
    	public Memento(String state) {
    		this.state=state;
    	}
    	public String getState() {
    		return state;
    	}
    }
    //创建者,创建备忘录
    class Originator {
    	private String state;
    	public void setState(String state) {
    		this.state=state;
    		//打印状态
    		System.out.println("现在的状态为:"+state);
    	}
    	public String getState() {
    		return state;
    	}
    	//创建备忘录,并存储数据
    	public Memento saveMemento() {
    		return new Memento(state);
    	}
    	//获取备忘录的数据,恢复备忘录时的状态
    	public void getStateFromMemento(Memento memento) {
    		this.state=memento.getState();
    	}
    }
    //管理者,备忘录的管理
    import java.util.ArrayList;
    class Caretaker {
    	//聚合备忘录
    	private ArrayList<Memento> mementos=new ArrayList<>();
    	//添加备忘录
    	public void addMemento(Memento memento) {
    		mementos.add(memento);
    	}
    	public Memento getMemento(int index) {
    		return mementos.get(index);
    	}
    }
    //测试类
    public class Client {
    	public static void main(String[] args) {
    		//定义创建类,管理类
    		Originator o=new Originator();
    		Caretaker c=new Caretaker();
    		//设置当时的状态
    		o.setState("状态1:攻击力:1000");
    		c.addMemento(o.saveMemento());
    		o.setState("状态2:攻击力:800");
    		c.addMemento(o.saveMemento());
    		String state=c.getMemento(0).getState();
    		System.out.println("恢复后的状态为:"+state);
    	}
    }
    

    备忘录模式能够随时恢复到备忘录保存的状态,而命令模式需要多个撤销恢复上一个状态。备忘录模也会用在存盘功能,将会使用到序列化和反序化,对于序列化和反序列化的相关使用可以查看这里:

    序列化和反序列化的相关知识

    可以尝试实现这个功能,这里就不展开介绍了。

    21、命令模式(Command)

    需要回退(撤销,Undo)状态的时候通常会使用到命令模式
    多次撤销,需要结合责任链模式
    宏命令,需要树状结构的组合模式结合使用

    命令的调用者和命令的接收者进行了解耦

    角色:

    • invok 调用者
    • Command 命令
    • Receive 接收者

    命令者模式(小庄)
    下面代码是对单个命令的执行和撤销作用

    //抽象化命令接口
    interface Command {
    	void execute();
    	void undo();
    }
    //具体命令
    class Command1 implements Command{
    	private Receive receive; 
    	String str="https://blog.csdn.net/weixin_44715643?spm=1000.2115.3001.5343";
    	public Command1(Receive receive) {
    		this.receive=receive;
    	}
    	@Override
    	public void execute() {
    		System.out.println("接收者的内容:"+receive.msg);
    		receive.msg+=str;
    		System.out.println("接收者的内容发生了改变:"+receive.msg);
    	}
    	@Override
    	public void undo() {
    		//回退到原来的状态
    		receive.msg=receive.msg.substring(0, receive.msg.length()-str.length());
    		System.out.println("恢复回上一步的操作时的接收者的内容:"+receive.msg);
    	}
    }
    //接收者,对命令做出相应的动作
    class Receive {
    	 String msg=new String("hello everybody ");
    }
    //调用者,聚合了命令
    class Invok {
    	private Command command;
    	public Invok(Command command) {
    		this.command=command;
    	}
    	public void executeCommand() {
    		command.execute();
    	}
    	public void undoCommand() {
    		command.undo();
    	}
    }
    //使用命令者
    public class Client {
    	public static void main(String[] args) {
    		Invok invok=new Invok(new Command1(new Receive()));
    		//执行操作
    		invok.executeCommand();
    		//撤销操作
    		System.out.println("下面进行撤销操作");
    		invok.undoCommand();
    	}
    }
    

    多个命令的执行和撤销

    //抽象化命令接口
    abstract class Command {
    	abstract int getIndex();
    	abstract void execute();
    	abstract Command undo();
    }
    //命令1
    import java.util.ArrayList;
    class Command1 extends Command{
    	private ArrayList<String> alist=new ArrayList<>();
    	private int index=-1;
    	private Receive receive; 
    	//增加的内容
    	String str="xxx ";
    	public Command1(Receive receive) {
    		this.receive=receive;
    	}
    	@Override
    	public void execute() {	
    		System.out.println("接收者的内容:"+receive.msg);
    		receive.msg+=str;
    		System.out.println("接收者的内容发生了改变:"+receive.msg);
    		alist.add(receive.msg);
    		index++;
    	}
    	@Override
    	public Command1 undo() {
    		if(index>=0) {
    			if(index==0) {
    				receive.msg=receive.msg.substring(0,receive.chushizhi.length());
    				alist.remove(index);
    				index--;
    				System.out.println("初始值为:"+receive.msg);
    			}else {
    				//回退到上一步的状态
    				alist.remove(index);
    				//下标往前面移动一位
    				String s=alist.get(--index);
    				receive.msg=receive.msg.substring(0, s.length());
    				System.out.println("恢复回上一步的操作时的接收者的内容:"+receive.msg);
    			}
    		}else {
    			System.out.println("到底了,不能再撤销了");
    		}
    		return this;
    	}
    	//记录当前的状态
    	@Override
    	public int getIndex() {
    		// TODO Auto-generated method stub
    		return index;
    	}
    }
    //接收者,对命令做出相应的动作
    class Receive {
    	 String msg="hello everybody ";
    	//记录初始值
    	String chushizhi=msg;
    }
    //调用者,可以作为使用者Client
    class Invok {
    	private Command command;
    	public Invok(Command command) {
    		this.command=command;
    	}
    	public void executeCommand() {
    		command.execute();
    	}
    	public Invok undoCommand() {
    		command.undo();
    		return this;
    	}
    }
    //使用命令者
    public class Client {
    	public static void main(String[] args) {
    		Invok invok=new Invok(new Command1(new Receive()));
    		//执行操作
    		invok.executeCommand();
    		invok.executeCommand();
    		invok.executeCommand();
    		//撤销操作
    		System.out.println("下面进行撤销操作");
    		invok.undoCommand();
    		invok.undoCommand();
    		invok.undoCommand();
    		invok.undoCommand();
    	}
    }
    

    22、访问者模式(Visitor)

    数据结构和数据分离

    角色:

    • Visitor 访问者抽象类 - Element 具体元素
    • Struct 结构,由多个元素组成
    • 具体元素

    访问者模式

    //抽象化接口
    interface Visitor {
    	void visitCpu(Cpu cpu);
    	void visitMemory(Memory memory);
    	void visitBoard(Board board);
    }
    //具体访问者
    class Woman implements Visitor{
    	//全程七折半
    	double totalPrice=0.0;
    	@Override
    	public void visitCpu(Cpu cpu) {
    		//打折活动
    		totalPrice+=cpu.getPrice()*0.75;
    	}
    	@Override
    	public void visitMemory(Memory memory) {
    		//打折活动
    		totalPrice+=memory.getPrice()*0.75;
    	}
    
    	@Override
    	public void visitBoard(Board board) {
    		//打折活动
    		totalPrice+=board.getPrice()*0.75;
    	}
    	//获取价钱
    	public double getPrice() {
    		return totalPrice;
    	}
    }
    //具体访问者
    class Man implements Visitor{
    	//购买需要花费的总价钱,全场八折!
    	double totalPrice=0.0;
    	@Override
    	public void visitCpu(Cpu cpu) {
    		//打折活动
    		totalPrice+=cpu.getPrice()*0.8;
    	}
    	@Override
    	public void visitMemory(Memory memory) {
    		totalPrice+=memory.getPrice()*0.8;
    	}
    	@Override
    	public void visitBoard(Board board) {
    		totalPrice+=board.getPrice()*0.8;
    	}
    	//获取价钱
    	public double getPrice() {
    		return totalPrice;
    	}
    }
    //结构
    interface ComputerPart {
    	void accept(Visitor visitor);
    }
    class Board implements ComputerPart{
    	//接待指定的访问人,调用指定的方法
    	@Override
    	public void accept(Visitor visitor) {
    		visitor.visitBoard(this);
    	}
    	//设置Board的单价
    	public double getPrice() {
    		return 1000.00;
    	}
    }
    class Cpu implements ComputerPart{
    	//接待指定的访问人,调用指定的方法
    	@Override
    	public void accept(Visitor visitor) {
    		visitor.visitCpu(this);
    	}
    	//设置Cpu的单价
    	public double getPrice() {
    		return 800.0;
    	}
    }
    class Memory implements ComputerPart{
    	//接待指定的访问人,调用指定的方法
    	@Override
    	public void accept(Visitor visitor) {
    		visitor.visitMemory(this);
    	}
    	//设置Memory的单价
    	public double getPrice() {
    		return 400.00;
    	}
    }
    //结构类,根据不同的访问者设定标配的电脑配件
    public class Computer {
    	Cpu cpu=new Cpu();
    	Memory memory=new Memory();
    	Board board=new Board();
    	//给访问者设置标配
    	public void accept(Visitor visitor) {
    		this.cpu.accept(visitor);
    		this.memory.accept(visitor);
    		this.board.accept(visitor);
    	}
    }
    //测试类
    public class Test {
    	public static void main(String[] args) {
    		//定义相关类
    		Computer computer=new Computer();
    		Man man=new Man();
    		Woman woman=new Woman();
    		//通过访问者模式进行使用
    		//一个男人过来购买电脑
    		computer.accept(man);
    		System.out.println("男人过来买需要花费:"+man.getPrice());
    		//一个女人过来购买电脑
    		computer.accept(woman);
    		System.out.println("女人过来买需要花费:"+woman.getPrice());		
    	}
    }
    

    23、解释器模式(Interpreter)

    对于一些固定文法构建一个解释句子的解释器。在实际开发中,我们很少会去开发一个解释器,因为涉及到的知识点比较复杂。所以我们只需要了解即可,我们通过加减法的例子去了解它

    角色:

    • Expression 抽象化解释器
    • Context 上下文,用于存放变量和值
    • Addition 加法解释器(具体解释器)
    • Minus 减法解释器(具体解释器)
    • Variable 变量解释器 (具体解释器),它主要和上下文打交道(获取变量指定的值)

    解释器模式(小庄)

    //抽象化解释器
    abstract class Expression {
    	//解释方法
    	public abstract int interpret(Context context);
    }
    //导入相关的包
    import java.util.HashMap;
    //上下文
    class Context {
    	//定义一个键值对的容器
    	private HashMap<Variable,Integer> map=new HashMap<>();
    	//把变量名和值存放到容器中
    	public void addVariable(Variable v,Integer x) {
    		map.put(v, x);
    	}
    	//通过变量名获取值
    	public int getVlaue(Variable v) {
    		return map.get(v);
    	}
    }
    //变量类,继承解释器
    class Variable extends Expression{
    	//给每个变量定义一个名字
    	String name;
    	//通过构造方法给变量名赋值
    	public Variable(String name) {
    		this.name=name;
    	}
    	//变量解释方法,通过变量获取值
    	@Override
    	public int interpret(Context context) {
    		//通过变量名获取上下文的容器中对应的值
    		return context.getVlaue(this);
    	}
    	//重写toString()方法
    	@Override
    	public String toString() {
    		return name;
    	}
    }
    //具体解释器,加法器
    class Addition extends Expression{
    	//定义两个解释器的类,表示传进来的变量
    	private Expression left;
    	private Expression right;
    	//存放进去的是变量
    	public Addition(Expression left,Expression right) {
    		this.left=left;
    		this.right=right;
    	}
    	//进行加法运算
    	@Override
    	public int interpret(Context context) {
    		//通过变量的解释器,获取对于的值,进行运算
    		return left.interpret(context)+right.interpret(context);
    	}
    	@Override
    	public String toString() {
    		//重写toString方法
    		return "("+left.toString()+"+"+right.toString()+")";
    	}
    }
    //具体解释器,模拟减法器
    class Minus extends Expression{
    	//定义两个解释器变量
    	private Expression left;
    	private Expression right;
    	//通过构造函数的方式传入两个变量
    	public Minus(Expression left,Expression right) {
    		this.left=left;
    		this.right=right;
    	}
    	//解释方法
    	@Override
    	public int interpret(Context context) {
    		//通过变量的解释器,获取对于的值,进行运算
    		return left.interpret(context)-right.interpret(context);
    	}
    	@Override
    	public String toString() {
    		//重写toString()方法
    		return "("+left.toString()+"-"+right.toString()+")";
    	}
    }
    //测试类
    public class Client {
    	public static void main(String[] args) {
    		//创建上下文
    		Context context=new Context();
    		//创建变量
    		Variable a=new Variable("a");
    		Variable b=new Variable("b");
    		Variable c=new Variable("c");
    		Variable d=new Variable("d");
    		//将变量赋予值,并保存到上下文
    		context.addVariable(a, 1);
    		context.addVariable(b, 2);
    		context.addVariable(c, 3);
    		context.addVariable(d, 4);
    		//设置表达式
    		Expression expression=new Addition(a,new Minus(b,new Addition(c,d)));
    		//对表达式进行解释
    		int result=expression.interpret(context);
    		//打印输出结果
    		System.out.println(expression.toString()+"="+result);
    	}
    }
    

    结语

    通过上面的学习,我们发现很多设计模式都十分相似,其实这很正常,因为这些设计模式都是基于Java的继承、多态、聚合、组合等方式完成的。我们应该学习这些模式的设计思想,它们适合在什么场景,并且我们不需要归根到底是哪个设计模式,只要我们类的设计的合理,达到我们的需求即可。通过学习这个知识点,我们能够更加深入的理解开发框架的底层设计原理,有助于我们后面的学习。
    我是小庄,欢迎大家和我一起成长。

    展开全文
    weixin_44715643 2021-09-06 22:16:50
  • 设计模式分为有创建型,结构型,行为型。 创建型:单例设计模式,工厂设计模式,抽象工厂模式,建造者模式,原型模式 结构型:代理模式,组合模式,享元模式,适配器模式,桥接模式,门面模式,装饰器模式, 行为...
    1. 设计模式可以分为哪几类,分别有哪些设计模式?(10分)
      设计模式分为有创建型,结构型,和行为型。
      创建型:单例设计模式,工厂设计模式,抽象工厂模式,建造者模式,原型模式
      结构型:代理模式,组合模式,享元模式,适配器模式,桥接模式,门面模式,装饰器模式,
      行为型:委派模式,策略模式,模板方法模式,观察者模式,访问者模式,责任链模式,迭代器模式,中介模式

    2.设计模式有哪些设计原则?(10分)
    (1)单一职责,一个类只负责一项自己业务,不做其他类的改变操作
    (2)开闭原则,对修改关闭,对扩展开放
    (3)依赖倒置原则,高层不依赖于细节,细节和高层都依赖抽象。
    (4)接口隔离原则, 细化接口分类,避免接口有过多方法兼容过多不同类的方法,尽量抽象。
    (5)里氏替换原则,子类不能改变父类方法的逻辑。
    (6)迪米特法则,最少知道原则,只负责自己业务线下操作,尽量减少对其他类的依赖。
    (7)合成复用原则,尽量使用组合和聚合,减少对继承使用产生的依赖。

    3.什么是简单工厂模式、工厂方法模式、抽象工厂模式?(5分)
    简单工厂:按照业务需求直接创建所需要的类,简单工厂需要实现接口,按照接口规定内创建相关对象。遵循了依赖倒置原则但违背了单一职责原则。所有构建对象全部由一个工厂完成。
    工厂方法:在简单工厂之上进行一次分类升级,每个类型创建都有自己的单独的工厂。但遵循统一的工厂方法且创建不同类型的对象。遵循了单一职责和依赖倒置原则。
    抽象工厂方法:在工厂方法之上整改再次升级,不仅仅对产品构建而是针对产品族进行构建,不但可以创建各种产品本身并且可以生产不同类型的产品。增加了一个生产维度更好的进行松耦合,但弊端如果基本抽象接口发生了业务改变,那么所有实现此抽象的抽象方法和产品组抽象类全部发生改变。故此要谨慎使用。

    1. 什么是单例模式,并说出2到3中不同的创建方式以及他们的优缺点?(15分)
      单例模式是保证全局使用同一个对象,保证数据的安全隔离和避免对内存的消耗。单例设计模式创建方法有如下4种:
      (1).饿汉式:从系统初始化时便将对象进行创建完成加载到内存中以备后用,优点是代码简单,没有任何线程安全问题。缺点是容易对内存造成浪费。
      (2).懒汉式:从第一次调用实例化时候进行创建一次,而后所有调用都用第一次创建的对象。优点是对内存浪费情况减轻,缺点会发生线程安全。优化线程安全有双重检查锁和静态内部类两种,第一种对性能消耗大于第二种,故选择第二种偏多。
      (3).枚举式:通过枚举类来进行判断从而单例创建,内部逻辑更像饿汉式,只不过饿汉式直接将指针指向了新的内存地址,而枚举式则在创建初期将所有要实例化的单例存放到了枚举内部的node中,node中的map存储了所有的单例。优点是无法通过反射等手段进行暴力破坏单例,缺点是系统启动时候就将所需要的实例化纳入到了内部的map集合中,也面临内存浪费的情况。,同时代码可读性也低。而后进行了改良便是Spring的IOC容器。
      (4).注册式:容器式存储单例,Spring创建实例容器也是使用的此种,将每次创建的对象存储到集合中,下次再次调用时候直接通过key来进行查找对应的单例。内存消耗上远远小于饿汉式,因内部是concurrntHashMap确保了线程安全性。可维护性和可读性也增强。使用Spring加载容器时默认都为容器的饿汉式加载方式,但也可以通过配置文件修改强制按照懒汉式方式进行加载。前者内存消耗大,但执行效率快,后者内存消耗小,执行效率低于前者。

    5.有哪些方式可以破坏单例模式,怎么解决单例破坏?(5分)
    破坏单例方式可为反射机制和序列以及反序列化等操作。对于反射机制可通过构造方法中进行单例判断如已存在可直接抛出异常,也可使用枚举单例创建方式从jdk底层杜绝反射破坏。对于反序列化类对象操作可重写Serilazible 中的readResolve()方法,来覆盖原有的判断逻辑解决反序列化带来的单例破坏。

    6.什么是浅克隆,什么是深克隆,实现深克隆的方式有哪些?(5分)
    浅克隆只是复制基础数据类型,对于引永型会复制原有地址到新对象中。所以浅克隆在修改引用型数据类型时会对原有引用型数据发生改变。
    深克隆对于潜克隆力度更大,不仅对基础数据类型,对引用数据类型也一并进行了拷贝并且将指针指向了新的地址让两者的引用数据类型进行独立分割。
    实现深克隆方式有三种一种是序列反序列化将对象重新指向内存从而达到深拷贝的效果,还一种是递归调用判断对象是否是引用,从而再次执行克隆达到深克隆目的。还一种是通过fastJson工具对需要克隆的对象转化成byte字节数组进行克隆。

    7.实现动态代理的方式有哪些,以及他们的区别?spring用的是哪个代理模式?(20分)
    动态代理分为静态代理和动态代理,动态代理又分为jdk实现和cglib实现方式。
    静态代理是传统代理模式,需要传入特定被代理的对象,并且手动调用代理方法等。
    动态代理一般指的是jdk代理,不用管被代理的对象类型,只要被代理对象实现和代理对象的接口即可。并且可以通过反射方式直接获取相关被代理的方法。
    jdk和cglib区别在于jdk通过反射方式调用接口下实现类,代理和被代理对象需要实现抽象接口。由于是反射实现,反射的内部校验机制导致执行速度会慢。但约束性比较强,必须实现接口。
    cglib不需要实现接口,它是直接通过反射和Asm框架生成三个不同类,来继承被代理的类重写被代理的方法。由于生成的Fastclass中保存了所有方法的索引,可以直接通过index索引找到对应方法,免去了反射的遍历所有方法方式提高执行效率。但因生成算法比jdk反射复杂故生成时间慢于jdk方式。
    Spring默认使用的是jdk形式,如果bean实现了接口则按jdk代理,如果没有则按照cglib生成。当然也可以修改配置文件默认使用cglib方式生成。

    8.什么是模板模式,它的作用在哪?(5分)
    模板模式,是流程化的一种方法模板,作用在于规范流程,能复用父类的共用方法,减少代码臃肿,可以通过内置钩子方法修改范式逻辑。如jdbc中的创建connection等都是用的模板方法.

    9.什么是策略模式?优点缺点在哪?(5分)
    封装了算法家族,在同一行为下传入不算法达到不同结果的方案模式。优点是符合开闭原则,避免了许多if else的复杂判断。缺点是调用者必须了解所有策略,并且会有诸多的策略类,增加维护成本。

    10.享元模式与注册式单例模式的区别?(5分)
    享元模式是结构型,注册式单例为创建型。两者其目的不同,享元模式针对的是对数据模式的反复复用,且有内部状态和外部状态,并非一成不变全部缓存之中,有可变数据。整体是一个对象池,在一定规则内可以调用相同数据的对象,减少对对象的反复创建带来的内存消耗。
    注册式单例在乎的是对对象本身的一个创建,确保每个单例都是独立存确保数据的安全性和一致性。容器内存储的所有单例都是独立的个体提供给不同对象使用,无法共用。

    11.什么是双亲委派?(5分)
    双亲委派是类加载时候用到的机制,原理为层级上传,子类加载时候先上交给父类判断是否存在如果不存在则继续向上报,直到最顶层后判断加载,如果不能加载则下沉到子类加载,如果再不能加载继续下沉直到最下级还不能加载则抛出异常,此方法优点可以确保对象数据的安全性,不会被恶意代码串改。

    12.委派模式跟代理模式的区别?(5分)
    委派模式更注重的是结果,遵循了迪米特法则,将任务下发后得到想要的结果,而不关心下级是如何实现的。代理关注的是过程,代理类需要封装被代理的方法,从而对方法前后进行一些处理达到代码强化的效果。

    13.什么是观察者模式?(5分)
    属于订阅-发布,源监听,实现了一对多的依赖关系。指一个元素可以被多个观察者类和对象监听,一旦被观察者发生了改变那么其观察者会自行进行发生改变。

    =========================
    1、请用自己的语言说出你对享元、组合、适配器、桥接模式的理解
    享元模式:就是共享对象池,在面临同一个数据反复创建对象时候可以使用享元模式,享元模式状态分为内部状态和外部状态两种,内部状态是不会发生改变,外部状态会根据周期发生改变。使用享元模式创建对象池从而达到减少对内存的消耗目的。属于结构型设计模式

    组合:适用于树形结构形式,观念在于“整体-分支”,两者可以相互依存,同时又能组合到一起成为上下级。主要组成为:抽象节点,树节点和叶子节点。实现模式分为透明组合模式和安全组合模式,透明组合模式在初创抽象类时候可以尽可能想到的方法全部加入,并且继承类全部共享方法,适合上下级行为动作相同的设计。安全组合模式主要针对上下级有诸多不同之处,下级需要定制不同自由行为的设计模式。

    适配器:适配器模式主要针对接口不匹配导致无法一起工作的两个类能进行并存工作。适配器实现构成有:目标角色,源角色,适配器三者组成。实现方式有类适配器,对象适配器和接口适配器,三者前两者针对类本身和传入对象本身的兼容,第三者则更为兼容。拥有诸多兼容方法,不限定某一兼容方法,但会出现许多空的实现方法,代码略显臃肿。属于结构型设计模式

    桥接模式:独立分离了抽象接口和具体实现类,让两者独立扩展,从而又相互依存。主要构成:抽象(Abstraction),修正抽象(RefinedAbstraction) ,实现(Implementor),具体实现(Concreteimplementor),因为没有用多继承,选用组合,从而遵循了开闭原则和里氏替换原则和依赖倒置原则,是个非常不错的设计模式。缺点是逻辑过于抽象不易理解。
    2、请说出享元与单例模式的区别
    享元模式和单例模式的容器单例非常相像,但两者角度不同,单例模式只是创建唯一对象,确保请求对象在内存中唯一。享元模式只是对象池对内部资源可以复用,外部资源发生改变时还是要创建新对象。单例模式针对类本身,只要是对象就要唯一,而享元模式则是关注逻辑的角度针对对象内元素的复用。

    1.委派模式与代理模式区别在哪?委派模式与策略模式区别?
    委派模式针对职责,具体业务由具体人员实现,遵循了单一职责和迪米特法则。是行为型模式,代理是结构型模式,针对行为的结果,对行为的过程进行增强处理。但委派模式没有对行为本身进行改变。
    委派模式是谁来做,有分配层级。策略模式是那种方式做,属平行同一级别执行。

    2.什么是双亲委派,双亲委派优势在哪?
    双亲委派在类加载过程中有使用,其意思是类加载器加载类的时候会先判断类是否加载过,如果没有加载过就向上级判断,直到顶层的bootStrapClassLoader,判断当前类可否加载如果可以加载直接加载,如果不能加载就下放下级加载,直到最下级依旧不能加载则抛出classNotFound错误。双亲委派优势在于保证class类不被非法程序窜改,就算窜改了也不能加载因为,就算加载了也不是原来的对象。保证了数据的操作安全。

    3.什么是模板方法模式,它的优点在哪里?
    模板方法模式是一种行为型模式,定义算法后由子类进行算法实现的一种设计模式。优点在于提取公共方法的复用和顺序规范。

    4.什么是责任链模式?
    属于行为型模式的一种。由多个单元执行各自任务最后组合一起完成所有逻辑的链式设计模式,每个单元执行完自己的方法判断后通过则下放到下一级单元执行,直到代码结束。优点在于易于扩展请求类,直接在链式中增加也删减逻辑判断即可,缺点是如责任链过长会影响性能,并且存在循环引用时会造成死循环,导致系统崩塌。

    1.用自己的语言总结什么是创建型模式,什么是结构型模式,什么是行为型模式?举例哪些设计模式是创建型,哪些是结构型,哪些是行为型
    创建型设计模式针对类的创建,以各种标准和手段达到不同形式的创建类的目的。结构型模式针对方法执行中,对方法本身梳理修饰和包装增强等。行为型模式针对结果,以不同角度来执行最终达到方法结束的目的。创建型主要分为单例,工厂,原型,建造者等。结构型为代理,享元,适配器等。行为型有委派,组合,策略,状态,模板方法等。
    2.单例写法有哪些,各自有哪些特点问题?
    单例有饿汉,懒汉,枚举,注册。饿汉对内存消耗大,但无线程安全问题。懒汉有线程问题但对内存开销相比饿汉更为节省,能被反射和序列反序列破解单例,枚举式无法被单例破解。注册式单例性能优于传统利用锁和内部加载累处理饿汉式效率高,同时更加灵活。
    3.什么是代理模式,有几种代理模式,区别在哪?
    代理模式是做一些预处理和后处理的手段达到对核心方法的一个强化,同时安全隔离核心操作。代理模式分为静态代理动态代理,动态又分为jdk和cglib的代理模式,区别在于静态代理针对传入的被代理对象和代理方法都是手动操作执行,动态代理可以不知道什么方法和类直接自动执行。jdk通过反射调用接口下的实现类,cglib是通过asm框架创建新的类继承被代理类并且通过索引能快速定位方法位置避免全局遍历方法查找,所以执行速度高于jdk.但因创建过程繁琐,创建过程时间比jdk要长。
    4.深克隆有哪几种方法?
    可以通过序列化反序列让所有参数指针指向新地址,也可以通过内部迭代实现深克隆模式。

    展开全文
    weixin_42243829 2021-02-28 22:37:39
  • 2、原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个原型类似的新实例。 3、工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。 4、抽象工厂...

    1、单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。

    2、原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。

    3、工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。

    4、抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。

    5、建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。

    6、代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。

    7、适配器(Adapter)模式:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

    8、桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合。

    9、装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。

    10、外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。

    11、享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。

    12、组合(Composite)模式:将对象组合成树状层次结构,使用户单个对象和组合对象具有一致的访问性。

    13、模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下定义该算法的某些特定步骤。

    14、策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。

    15、命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分隔开。

    16、职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。

    17、状态(State)模式:允许一个对象在其内部状态改变时改变其行为能力。

    18、观察者(Observer)模式:多个对象间一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。

    19、中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。

    20、迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

    21、访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。

    22、备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。

    23、解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

     

    一、开闭原则

    定义:一个软件实体,如类,模块和函数应该对拓展开放,对修改关闭。用抽象构建框架,用实现拓展细节。

    优点:提高软件系统的可复用性及可维护性。

    二、依赖倒置原则

    定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象。抽象不应该依赖细节;细节应该依赖抽象。针对接口编程,不要针对实现编程

    优点:可以减少类之间的耦合性、提高系统稳定性、提高代码可读性和可维护性,可降低修改程序所造成的风险。

    三、单一职责原则

    定义:不要存在多于一个导致类变更的原因。一个类\接口\方法只负责一项职责。

    优点:降低类的复杂度、提高类的可读性,提高系统的可维护性、降低变更引起的风险。

    四、接口隔离原则

    定义:用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。一个类对一个类的依赖应该建立在最小的接口上。建立单一接口,不要建立庞大臃肿的接口。尽量细化接口,接口中的方法尽量少。

    优点:符合我们常说的高内聚低耦合的设计思想,从而使得类具有良好的可读性、可拓展性和可维护性。

    五、迪米特原则

    定义:一个对象应该对其他对象保持最少的了解。又叫最少知道原则。尽量降低类与类之间的耦合。强调只和朋友交流,不和陌生人说话。朋友--出现在成员变量、方法的输入、输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。

    优点:降低类之间的耦合。

    六、里式替换原则

    定义:如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都替换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。

    定义拓展:一个软件实体如果适用一个父类的话,那一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。

    引申意义:子类可以拓展父类的功能,但不能改变父类原有的功能。

    含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

    含义2:子类中可以增加自己特有的方法。

    含义3:当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。

    含义4:当子类的方法实现父类的方法时(重写\重载或实现抽象方法),方法的后置条件(即方法的输出\返回值)要比父类更严格或相等。

    优点1:约束继承泛滥,开闭原则的一种体现。

    优点2:加强程序的健壮性,同时变更时也可以做到非常好的兼容性,提高程序的维护性、拓展性。降低需求变更时引入的风险。

    七、合成(组合)/聚合复用原则

    定义:尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的。

    优点:可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。

    八、简单工厂

    定义:由一个工厂对象决定创建出哪一种产品类的实例。

    类型:创建型,但不属于GOF23种设计模式。

    使用场景:工厂类负责创建的对象比较少。客户端(应用层)只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心。

    优点:只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建的细节。

    缺点:工厂类的职责相对过重,增加新的产品,需要修改工厂类的判断逻辑,违背开闭原则。

    九、工厂方法

    定义:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。

    适用场景:创建对象需要大量重复的代码。客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。一个类通过其子类来指定创建哪个对象。

    优点:用户只需要关心所需的产品对应的工厂,无需关心创建细节。加入新产品符合开闭原则,提高可拓展性。

    缺点:类的个数容易过多,增加复杂度。增加了系统的抽象性和理解难度。

    十、抽象工厂

    定义:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口。无需指定它们具体的类。

    使用场景:客户端(应用层)不依赖于产品实例如何被创建、实现等细节。强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

    优点:具体产品在应用层代码隔离,无需关心创建细节。将一个系列的产品族统一到一起创建。

    缺点:规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。增加了系统的抽象性和理解难度。

     

    十一、建造者模式

    定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。用户只需指定所要建造的类型就可以得到它们,建造过程及细节不需要知道。

    适用场景:如果一个对象有非常复杂的内部结构(很多属性)。想把复杂对象的创建和使用分离。

    优点:封装性好,创建和使用分离。扩展性好、建造类之间独立、一定程度上解耦。

    缺点:产生多余的Builder对象。产品内部发生变化,建造者都要修改,成本较大。

    十二、单例设计模式

    定义:保证一个类仅有一个实例,并提供一个全局的访问点。

    适用场景:想确保任何情况下都绝对只有一个实例。

    优点:在内存里只有一个实例,减少了内存的开销。可以避免对资源的多重占用。设置全局访问点,严格控制访问。

    缺点:没有接口,扩展困难。

    重点:私有构造器。线程安全。延迟加载。序列化和反序列化安全。反射。

    十三、原型模式

    定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。不需要知道任何创建的细节,不调用构造函数

    适用场景:类初始化消耗较多资源。new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)。构造函数比较复杂。循环体中产生大量对象时。

    优点:原型模式性能比直接new一个对象性能高。简化创建过程。

    缺点:必须配备克隆方法。对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险。深拷贝、浅拷贝要运用得当。

    十四、外观模式

    定义:又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。

    使用场景:子系统越来越复杂,增加外观模式提供简单调用接口。构建多层系统结构,利用外观对象作为每层的入口,简化层间调用。

    优点:简化了调用过程,无需了解深入子系统,防止带来风险。减少系统依赖、松散耦合。更好的划分访问层次。符合迪米特法则,即最少知道原则。

    缺点:增加子系统、扩展子系统行为容易引入风险。不符合开闭原则。

    十五、装饰者模式

    定义:在不改变原有对象的基础上,将功能附加到对象上。提供了比继承更有弹性的替代方案(扩展原有对象功能)。

    优点:继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能。通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同的效果。符合开闭原则。

    缺点:会出现更多的代码,更多的类,增加程序复杂性。动态装饰时,多层装饰时会更复杂。

    十六、适配器模式

    定义:将一个类的接口转换成客户期望的另一个接口。使原本接口不兼容的类可以一起工作。

    适用场景:已经存在的类,它的方法和需求不匹配时(方法结果相同或相似)。不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。

    优点:能提高类的透明性和复用,现有的类复用但不需要改变。目标类和适配器类解耦,提高程序扩展性。符合开闭原则。

    缺点:适配器编写过程需要全面考虑,可能会增加系统的复杂性。增加系统代码可读的难度。

    十七、享元模式

    定义:提供了减少对象数量从而改善应用所需的对象结构的方式。运用共享技术有效地支持大量细粒度的对象

    适用场景:常常应用于系统底层的开发,以便解决系统的性能问题。系统有大量相似对象、需要缓冲池的场景。

    优点:减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率。减少内存之外的其他资源占用。

    缺点:关注内/外部状态、关注线程安全问题。使系统、程序的逻辑复杂化。

    十八、组合模式

    定义:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式是客户端对单个对象和组合对象保持一致的方式处理。

    适用场景:希望客户端可以忽略组合对象与单个对象的差异时。处理一个树形结构时。

    优点:清楚地定义分层次的复杂对象,表示对象的全部或部分层次。让客户端忽略了层次的差异,方便对整个层次结构进行控制。简化客户端代码。符合开闭原则。

    缺点:限制类型时会较为复杂。使设计变得更加抽象。

    十九、桥接模式

    定义:将抽象部分与它的具体实现部分分离,使它们都可以独立地变化。通过组合的方式建立两个类之间关系,而不是继承。

    适用场景:抽象和具体实现之间增加更多的灵活性。一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行拓展。不希望使用继承,或因为多层继承导致系统类的个数剧增。

    优点:分离抽象部分及其具体实现部分。提高了系统的可扩展性。符合开闭原则。符合合成复用原则。

    缺点:增加了系统的理解与设计难度。需要正确地识别出系统中两个独立变化的维度。

    二十、代理模式

    定义:为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用。

    适用场景:保护目标对象。增强目标对象。

    优点:代理模式能将代理对象与真实被调用的目标对象分离。一定程度上降低了系统的耦合度,扩展性好。保护目标对象。增强目标对象。

    缺点:代理模式会造成系统设计中类的数目增加。在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。增加系统的复杂度。

    二十一、模板模式

    定义:定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

    适用场景:一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。各子类中公共的行为被提取出来并集中到一个公共父类中,从而避免代码重复。

    优点:提高复用性,提高扩展性,符合开闭原则。

    缺点:类数目增加。增加了系统实现的复杂度。继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。

    二十二、迭代器模式

    定义:提供一种方法,顺序访问一个集合对象中的各个元素,而又不暴露该对象的内部表示。

    适用场景:访问一个集合对象的内容而无需暴露它的内部表示。为遍历不同的集合结构提供一个统一的接口。

    优点:分离了集合对象的遍历行为。

    缺点:类的个数成对增加。

    二十三、策略模式

    定义:定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化不会影响到使用算法的用户。

    适用场景:系统有很多类,而它们的区别仅仅在于它们的行为不同。一个系统需要动态地在几种算法中选择一种。

    优点:符合开闭原则。避免使用多重条件转移语句。提高算法的保密性和安全性。

    缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类。产生很多策略类。

    二十四、解释器模式

    定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。为了解释一种语言,而为语言创建的解释器。

    适用场景:某个特定类型问题发生频率足够高。

    优点:语法由很多类表示,容易改变及扩展此"语言"。

    缺点:当语法规则数目太多时,增加了系统复杂度。

    二十五、观察者模式

    定义:定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新。

    适用场景:关联行为场景,建立一套触发机制。

    优点:观察者和被观察者之间建立一个抽象的耦合。观察者模式支持广播通信。

    缺点:观察者之间有过多的细节依赖、提高时间消耗及程序复杂度。使用要得当,要避免循环调用。

    二十六、备忘录模式

    定义:保存一个对象的某个状态,以便在适当的时候恢复对象。“没有后悔药”。“存档”。

    适用场景:保存及恢复数据相关业务场景。后悔的时候,即想恢复到之前的状态。

    优点:为用户提供一种可恢复机制。存档信息的封装。

    缺点:资源占用。

    二十七、命令模式

    定义:将"请求"封装成对象,以便使用不同的请求。命令模式解决了应用程序中对象的职责以及它们之间的通信方式。

    适用场景:请求调用者和请求接收者需要解耦,使得调用者和接受者不直接交互。需要抽象出等待执行的行为。

    优点:降低耦合。容易扩展新命令或一组命令。

    缺点:命令的无限扩展会增加类的数量,提高系统实现复杂度。

    二十八、中介者模式

    定义:定义一个封装一组对象如何交互的对象。通过使对象明确地相互引用来促进松散耦合,并允许独立的改变它们的交互。

    适用场景:系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。交互的公共行为,如果需要改变行为则可以增加新的中介类。

    优点:将一对多转换成了一对一,降低程序复杂度。类之间解耦。

    缺点:中介者过多,导致系统复杂。

    二十九、责任链模式

    定义:为请求创建一个接受此次请求对象的链。

    适用场景:一个请求的处理需要多个对象当中的一个或几个协作处理。

    优点:请求的发送者和接受者(请求的处理)解耦。责任链可以动态组合。

    缺点:责任链太长或者处理时间过长,影响性能。责任链有可能过多。

    三十、访问者模式

    定义:封装作用于某数据结构(如List/Set/Map等)中的各元素的操作。可以在不改变各元素的类的前提下,定义作用于这些元素的操作。

    适用场景:一个数据结构如(List/Set/Map等)包含很多类型对象。数据结构与数据操作相分离。

    优点:增加新的操作很容易,即增加一个新的访问者。

    缺点:增加新的数据结构困难。具体元素变更比较麻烦。

    三十一、状态模式

    定义:允许一个对象在其内部状态改变时,改变它的行为。

    适用场景:一个对象存在多个状态(不同状态下行为不同),且状态可相互转换。

    优点:将不同的状态隔离。把各个状态的转换逻辑,分布到State的子类中,减少相互间依赖。增加新的状态很简单。

    缺点:状态多的业务场景导致类数目增加,系统变复杂。

     

    展开全文
    qq_39251691 2021-01-27 21:06:01
  • weixin_39790686 2020-12-24 23:30:44
  • Edison_x04 2021-12-14 20:26:33
  • haqiang555 2021-04-09 15:01:27
  • weixin_43520670 2021-05-27 19:49:04
  • bokerr 2021-03-26 00:45:33
  • weixin_30839665 2020-12-24 23:30:41
  • zhq9695 2021-01-05 10:42:27
  • lzqiang2011 2021-01-29 09:27:29
  • owanttofly 2021-10-24 19:05:40
  • PILIpilipala 2021-02-20 13:45:33
  • syc0616 2021-03-09 22:22:56
  • mengchuan6666 2021-07-23 11:41:12
  • chengbaobao520 2021-10-04 21:02:40
  • BedivereAlter 2021-03-21 17:15:08
  • weixin_35823536 2020-12-24 02:44:34
  • fggsgnhz 2021-03-31 21:40:18
  • weixin_42373789 2020-12-24 12:27:23
  • weixin_30417063 2021-05-14 20:47:40
  • lin_dark 2021-04-12 21:44:30
  • weixin_44497034 2021-02-22 12:30:53
  • qq_41824825 2021-11-29 20:15:08
  • weixin_33953395 2021-06-10 02:24:02
  • qq_39368007 2021-02-24 10:36:51
  • qq_42411307 2021-03-07 20:59:17
  • weixin_44954070 2021-06-13 21:30:38
  • qq_45590334 2020-12-30 22:21:58

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 405,686
精华内容 162,274
关键字:

和安全相关的设计模式