zoukankan      html  css  js  c++  java
  • 《第六次作业》

    第一章 设计模式相关内容介绍

    1.1  设计模式简介

      设计模式描述了软件设计过程中某一类常见问题的一般性的解决方案。而面向对象设计模式描述了面向对象设计过程中特定场景下、类与相互通信的对象之间常见的组织关系。

    1.2  GoF 23种设计模式

    该模式是学习面向对象设计模式的起点。但并非终点。也并非其就表示了所有的“面向对象设计模式”。

    1.3  面向对象设计模式

          面向对象设计模式解决的是“类与相互通信的对象之间的组织关系”,包括它们的角色、职责、协作方式几个方面。

          面向对象设计模式是“好的面向对象设计”,所谓“好的面向对象设计”是那些可以满足“应对变化,提高复用”的设计。

          面向对象设计模式描述的是软件设计,因此它是独立于编程语言的,但是面向对象设计模式的最终实现仍要使用面向对象编程语言来表达

    ▲从宏观层面看,面向对象的构建方式更能适应软件的变化,能将变化所带来的影响减为最小。

    从微观层面来看,面向对象的方式更强调各个类的责任,新增成员类型不会影响原来员工类型的实现代码。

    1.4  面向对象的三大特征

        1、封装:隐藏内部实现 

         2、继承:复用现有代码  

         3、多态:改写对象行为

    1.5  对象

         从概念层面讲,对象是某种拥有责任的抽象。

         从规格层面讲,对象是一系列可以被其他对象使用的公共接口。

         从语言实现层面来看,对象封装了代码和数据。

       ▲怎样才能设计“好的面向对象”?

         遵循一定的面向对象设计原则。

         熟悉一些典型的面向对象设计模式。

    1.6  设计模式的三大原则

    1、针对接口编程,而不是针对实现编程    客户不需要知道使用对象的特定类型,只需要知道对象拥有客户所期望的接口。

    2、优先使用对象组合,而不是类继承    继承在某种程度上破坏了封装性,子类父类耦合度高;而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

    3、封装变化点,封装的代码不会影响其他代码, 实现城市间的松耦合。

    ▲敏捷软件开发实践提倡的是“Perfactoring to Patterns”是目前普遍公认的最好的使用设计模式的方法。

    1.7  设计原则(5)

     

    1.8 设计模式的分类:


    第二章 创建者模式

    1.1 分类

    2.1 单例模式

    满足一个类只有一个实例,并且这个实例具有全局访问权限。

    2.2 单例模式的结构

    1. 单例类:只能创建一个实例的类。
    2. 访问类:使用单例类。

    2.3 单例模式的优缺点

     优点:

      1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。

      2、避免对资源的多重占用(比如写文件操作)。

     缺点:

      没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

      2.4 单例模式的使用场景

    1.  要求生产唯一序列号。
    2.   WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
    3.   创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

    2.5 单例模式的实现方式

    1. 单例设计模式分为两种:
    2.    饿汉式:类加载就会导致该单实例对象被创建。
    3.    懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建。
    4.   懒汉式(静态变量方式)
    5.  懒汉式(静态代码块方式)
    6.  懒汉式(线程不安全)
    7.   懒汉式(线程安全)
    8.   懒汉式(双重检查锁)
    9.   懒汉式(静态内部类方式)
    10.   枚举方式。

    3.1 原型模式

    原型模式用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。

    3.2 原型模式结构

    • 抽象原型类:规定了具体原型对象必须实现的接口。
    •  具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
    • 访问类:使用具体原型类中的 clone() 方法来复制新的对象。

    3.3 原型模式的优缺点

    优点:

    1.  Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
    2.   可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

    缺点:

    1.   需要为每一个类都配置一个clone()方法
    2.  clone()方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
    3.   当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。

    3.4  原型模式的使用场景:(原型模式比new新建对象性能要好)

                *需要一个类实例化大量的重复对象,或者数据重复性很大,极个别需要修改的属性。

                *对象初始化过程比较复杂。

                *在运行时刻不方便获取原型的类时,也可以通过原型模式来实现。

    3.5 原型模式的实现

    •   浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
    • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

    4.1 工厂方法模式

    对象的具体实现经常发生变化,但拥有比较稳定的接口。

    4.2 工厂方法模式的结构

    • 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
    •  抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
    •  具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
    •   指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

    4.3 工厂方法模式的优缺点

    优点:

    1.  封装性好,构建和表示分离。
    2.   扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
    3.  客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。

    缺点:

    1. 产品的组成部分必须相同,这限制了其使用范围。
    2.   如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

    4.4 工厂方法模式的使用场景

    1.   当一个类不知道它所必须创建的对象的类的时候
    2.   当一个类希望由它的子类来指定它所创建的对象的时候
    3.  当类将创建对象的职责委托给多个帮忙子类的中的某一个,并且你希望将哪一个帮助子类是代理者者一信息局部化的时候

    5.1 抽象工厂模式

    抽象工厂模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

    5.2抽象工厂模式结构

    •  抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
    •   具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
    •   抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
    •  具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

    5.3 抽象工厂模式的优缺点

    优点:

    1.  可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
    2.   当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
    3.  抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

    缺点:

     当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

    5.4 抽象工厂模式的使用场景

    1.  系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
    2.   系统一次只可能消费其中某一族产品,即同族的产品一起使用。

    6.1 建造者模式

    指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。

    6.2建造者模式的结构

    •  产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
    •   抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
    •   具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
    • 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

    6.3 建造者模式的优缺点

    优点:

    1. 封装性好,构建和表示分离。
    2.  扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
    3.  客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。

    缺点:

    1.  产品的组成部分必须相同,这限制了其使用范围。
    2.   如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

    6.4 建造者模式的使用场景

    1.  相同的方法,不同的执行顺序,产生不同的结果。
    2.   多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
    3.  产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
    4.   初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。

    第三章 行为型模式

    1.1 分类

    2.1 Proxy代理模式

    *定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问,这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

          *动机:实现间接访问使用者或系统结构。

          *需要解决的问题:

              ——如何在不失去透明操作对象的同时来管理/控制这些独享特有的复杂性?

                  回答:增加一层间接层是软件开发中常见的解决方式。

          *意图:为其他对象提供一种代理,用来控制对这个对象的访问。

          *分类:静态代理,动态代理。

          *结构:

    2.2  Proxy代理模式的要点:

           *作用:增加一层间接层,来间接访问某些对象。

           *实现:Proxy代理模式的实现方法、实现粒度都相差很大。

           *要求:满足实现间接控制,有时候舍弃透明性也可以接受。

    3.1  Adapter适配器模式:

    *定义:“适配”即转换,在不改变原有实现的基础上,将不兼容的接口改为兼容的接口。

    *动机:解决应用环境变化,现存对象在新环境下不兼容的问题。

    *需要解决的问题:

        1>如何解决“迁移的变化”?

        2>如何能利用现有对象的良好实现,又能满足新的应用环境要求的接口?

    *意图:将一个类的接口转换为客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

    *角色:适配器中的几种角色:

    @目标接口(Target): 当前系统业务所期待的接口,可以是抽象类或者接口

    @适配源类(Adaptee): 它是被访问和适配的现存组件库中的组件接口

    @适配者类(Adapter): 它是一个转换器,通过集成或引用适配者的对象,把适配源接口转换成目标接口,让客户按目标接口的格式访问适配者

    *结构:(一般使用对象适配器)

       ——对象适配器:使用组合的方案。

       ——类适配器:使用继承方式,容易混乱。

    3.2 适配器模式的要点:

       *应用环境:主要应用于“希望复用一些现存的类”,但是接口与复用环境要求不一致。在遗留代码复用,类库迁移等方面非常有用。

       *实现结构:两种实现结构:对象适配器、类适配器。但是类适配器采用“多继承”,会造成不良的高耦合,所以一般使用对象适配器实现。对象适配器采用“对象组合”的方式,更符合松耦合,更利于扩展。

        *使用:使用灵活,不用拘泥于两种结构。例如:可以将适配器模式中的“现存对象”作为新的接口方法参数,来达到适配器的目的。

    *实现:尽可能使用“面向接口”的风格。

    4.1 Bridge桥接模式:

          *定义:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。     

    *动机:将实现抽象部分与实现部分进行分离,使它们都可以独立地变化。

          *需要解决的问题:

            ——1>如何应对“多维度的变化”?

            ——2>如何利用面向对象的技术使Tank类型可以轻松地沿着“平台”和“型号”两个方向变化,而不引入额外的复杂度?

          *意图:将抽象部分与实现部分分离,让其可以独立地变化。

      *结构:桥接模式主要有以下角色:

            @抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。

    @扩展抽象化(Refined    Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。

    @实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。

    @具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。

    4.2 Bridge桥接模式的要点:

    1>使用“对象间的组合关系”来解耦抽象和实现之间固有的绑定关系,使得抽象(Tank的型号)和实现(不同的平台)可以沿着各自的维度变化。

    2>抽象和实现沿着各自维度的变化,就是“子类化”它们,得到各个子类后,便可以进行任意组合,从而获得不同平台上的不同型号。

    3>桥接模式类似于多继承,但桥接模式是比多继承方案更好的解决方法。

    4>一般应用在“两个非常强的变化维度”,有时候当两个变化不会导致纵横交错的结果,不一定要使用桥接模式。

    5.1 Decorator装饰者模式:

          *定义:在不改变现有对象结构的情况下,动态地给该对象增加一些职责(增加额外功能)的模式。

          *动机:解决由于用继承来引入,所以会导致出现多继承,缺乏灵活性的问题。

          *需要面临的问题:

             ——如何使“对象功能的扩展”能够根据需要来动态地实现(在运行时如何实现)?

             ——同时避免“扩展功能的增多”带来的子类膨胀问题?

             ——如何将“功能扩展变化”所导致的影响降为最低?

           *意图:动态地给一个对象增加一些额外的功能。从增加功能来说,装饰者模式比生成子类更加灵活。

           *角色:装饰者中的几种角色:

              @抽象构件(Component): 定义一个抽象接口以规范准备接收附加责任的对象

    @具体构件(ConcreteComponent): 实现抽象构件,通过装饰角色为其添加一些职责

    @抽象装饰(Decorator): 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能

    @具体装饰(ConcreteDecorator): 实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

    5.2  Decorator装饰者模式的要点:

          *作用:在运行时通过组合来动态地扩展对象功能,并且可以根据需要扩展多个功能。

          *角色:Component类在装饰模式中不应该去实现具体的行为,而且应该透明,透明即无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。

          *实现:装饰者模式在表现上是继承关系,但在实现上是组合关系。可以使用一个或者多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。

          *核心:解决主体类在多个方向上的扩展功能。

      6.1  Facade外观模式:

           *定义:外观模式又称为 门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式,该模式对外有一个统一的接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

           *动机:使得子系统更加容易使用。

           *需要解决的问题:

               ——如何简化外部客户程序和系统间的交互接口?

               ——如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦?

           *意图:定义一个高层接口,让这个接口使子系统更加容易使用。

           *角色:Facade外观模式的几种角色:

                  @外观(Facade)角色: 为多个子系统对外提供一个共同的接口

    @子系统(SubSystem)角色: 实现系统的部分功能,客户可以通过外观角色访问它

     6.2  Facade外观模式的要点:

    *作用:简化了整个组件系统的接口,同时将组件内部与外部客户程序进行“解耦”。

    *结构:注重架构设计。

    *区别:外观模式注重简化接口,适配器模式注重转换接口,桥接模式注重分离接口(抽象)与实现,装饰模式注重在有稳定接口的前提下,为对象扩展功能。

    7.1  Flyweight享元模式:

         *定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量,避免大量相似对象的开销,从而提高系统资源的利用率。

         *重点:区分应用中的内部状态、外部状态这两种状态,并将外部状态外部化。

         *作用:降低采用面向对象语言编程所带来的运行时代价,尽可能减少内存的使用。

         *需要解决的问题:如何避免大量细粒度对象所带来的开销问题?

         *意图:通过共享技术来支持大量细粒度的对象。

         *结构:

    7.2 Flyweight享元模式的要点:

          *作用:主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。

          *核心:通过对象共享的做法来降低系统中对象的个数,从而达到降低系统的内存压力。需要注意对象状态的处理。

          *问题:首先评估对象的大小,要根据具体应用情况进行评估,不能凭空想象。

      8.1 Composite组合模式:

          *定义:组合模式又称为整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

          *动机:解决客户端对内部实现结构依赖。

          *需要解决的问题:

            ——如何将客户代码与复杂的对象容器结构分离?

                回答:让对象容器自己来实现自身的复杂结构,从而达到客户代码就像处理简单对象一样来处理复杂的对象容器。

           *意图:使用户对单个对象和组合对象的使用具有一致性。

           *结构:客户端依赖于抽象接口,只有这一种依赖关系

    8.2 Composite组合模式的要点:

          *结构:采用树形结构来实现普遍的对象容器,从而将“一对多”关系转化为“一对一”的关系,客户端无需关心处理的是单个对象还是组合的对象容器。

          *核心:将客户代码与复杂的对象容器结构分离,分离后,客户代码将与并非对象容器的复内部实现结构发生依赖关系,从而方便应对变化。

          *方法位置:”Add”和”Remove”等是放置在表示抽象对象的Component类中,还是放置在表示对象容器的Composite类中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。有可能违背“单一职责原则”,但这是应用这种特殊结构的代价。

          *具体实现:可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可以采用缓存技巧(例如:new出来)来改善效率。


    第三章  行为型模式

     

    概述:

     

       行为型模式用于描述程序在运行时复杂的流程控制,简单来说,行为型模式就是用于描述多个类或对象之间如何相互协作来共同完成单个对象都无法完成的任务,它涉及算法和对象间的职责分配问题。

     

     

    分类:(11种,除了标注的模式外,其余都为对象行为模式)

     模板方法模式(类行为模式)

     策略模式

     命令模式

     职责链模式

     状态模式

     观察者模式

    中介者模式

    迭代器模式

     访问者模式

     备忘录模式

     解释器模式(类行为模式)

    1 模板方法模式

    1.1 定义

       定义一个操作中的算法骨架,而将算法中的一些步骤延迟到子类中,使得子类可以在不改变算法结构的前提下重新定义该算法的某些特定步骤

    1.2 结构

    •  抽象类:负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

          *模板方法:定义了算法的骨架,一般用final修饰,按某种顺序调用其包含的基本方法。

          *基本方法:是实现算法中各个步骤的方法,是模板方法的组成部分。基本方法又分为三种:

    1.         抽象方法:由抽象类声明,但由其子类具体实现。
    2.          具体方法:由抽象类或具体类声明并实现,其子类可以继承也可以重写。
    3.           钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
    • 具体子类:实现了抽象类中所有的抽象方法和钩子方法。

    1.3 优缺点

     优点

      *提高代码复用

        将相同的代码放入抽象的父类中,而将不同的代码放入子类中。

      *实现了反向控制

        通过一个父类调用子类的操作,通过对子类的具体实现进行扩展,实现了“反向控制”,并符合“开闭原则”。

     缺点:

          *对每个不同的实现都需要定义一个子类,会导致类的数量增加,系统更加庞大,设计也更加抽象。

         *父类中的抽象方法由子类实现,子类的执行结果会影响父类的执行结果,这导致一种反向的控制结构,增加了阅读代码的难度。

    1.4 适用场景

    1.      算法的整体步骤相对稳定,但其中的个别部分容易发生改变时,可以使用模板方法模式,将容易改变的部分抽象出来,让子类实现。
    2.    需要子类来决定父类中的某个步骤是否执行,实现子类对父类的反向控制。

    2 策略模式

    2.1 定义

          该模式定义了一系列算法,并把每个算法封装起来,这些算法可以相互替换,且算法的改变不会影响到使用算法的客户。

          策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和实现向隔离,并委派给不同的对象对这些算法进行管理。

    2.2 结构

    •  抽象策略类:这是一个抽象角色,通常由一个接口或抽象类实现,来给出所有具体策略类所需的接口(即方法)。
    •  具体策略类:实现了抽象策略类定义的接口,提供具体的算法实现或行为。
    •  环境类:持有一个策略类的引用,最终给客户端调用。

    2.3 优缺点:

         *优点:

    1.  策略类之间可以相互转换:由于策略类共同实现同一个接口,所以使它们之间可以自己转换。
    2. 便于扩展:增加一个新策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则”。
    3.  避免使用多重条件选择语句(if else):充分体现面向对象设计思想

         *缺点:

    1. 客户端必须知道所有的策略类,并决定使用哪一个具体的策略类。
    2.  将产生很多策略类的对象,可以通过享元模式在一定程度上减少对象的数量。

    2.4 使用场景

    1. 一个系统需要动态地选择其中一种算法时,可将每个算法封装到策略类中。
    2.  一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式实现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
    3.  要求各算法彼此完全独立,且要求对客户端隐藏内部具体算法的实现细节时。
    4.  要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
    5. 多个类只区别在表现行为不同,可以使用策略模式,在运行是动态选择具体要执行的行为。

    3 命令模式

    3.1 定义

         将一个请求封装为一个对象,使发出的请求和执行请求分隔开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。

    3.2 结构

    • 抽象命令角色:定义命令的接口、声明执行的方法。
    •  具体命令角色:具体的命令,实现命令接口;通常持有接受者,并调用接受者的功能来完成命令要执行的操作。
    • 调用者角色:要求命令对象执行请求,通常持有命令对象,命令对象的个数可以为多个。这个是客户端真正触发命令,并要求命令执行响应操作的地方,也就是想当于使用命令对象的入口。
    • 接受者角色:接收者,真正执行命令的对象。任何类都有可能成为接收者,只要它能够实现命令要求实现的相应功能。

    3.3 优缺点:

          *优点

    1.  降低耦合度:能将调用操作的对象与实现该操作的对象解耦。
    2.  便于扩展:增加或删除命令非常方便,采用命令模式增加与删除命令不会影响其他类,满足“开闭原则”,对扩展比较灵活。
    3.  可以实现宏命令:命令模式可以与组合模式相结合,将多个命令装配成一个组合命令,即宏命令。
    4.  方便实现撤销和恢复操作:命令模式可以与备忘录模式相结合,实现命令的撤销和恢复。

           *缺点

    1.  可能导致某些系统有过多的具体命令类。
    2.  使结构更加复杂。

    3.4 使用场景

    1. 需要将请求调用者和请求接受者进行解耦,使调用者和接受者不直接交互时,使用命令模式。
    2.  需要在不同的时间指定请求,将请求排队和执行请求时,使用命令模式。
    3.  需要支持命令的撤销操作和恢复操作时,使用命令模式。

    4 责任链模式

    4.1 定义

       责任链模式又名职责链模式,为了避免请求发送者和多个请求处理这耦合在一起,将所有请求的处理者通过前一个对象记住下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

    在日常生活中,常常会出现这样的情况:一个请求可以被多个对象处理,但每个对象的处理条件或权限不同。比如:一个员工要请假,应直接把请假单交给小组长审批,小组长能处理就审批,不能处理就将请假单转交给上一级进行审批,以此类推,直到有对象能够处理它。

    4.2 结构

    • 抽象处理者角色:定义一个处理请求的接口,包含抽象处理方法和一个后继链接。
    •  具体处理者角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理则处理,否则将该请求转给它的后继者。
    •  客户类角色:创建处理链,并向链头的具体处理者提交请求,它不关心处理细节和请求的传递过程。

    4.3 优缺点

     优点:

    1.  降低了耦合度:降低了请求发送者和请求接收者之间的耦合度。
    2. 增强了可扩展性:根据需要增加新的请求处理类,满足“开闭原则”。
    3. 增强了分配职责的灵活性:当工作流程发生变化,可以动态地改变链内的成员或者修改它们的顺序,也可以动态地新增或者删除责任。
    4.  简化了对象之间的连接:每一个对象只需要保持一个对其后继者的引用即可,避免了众多的条件判断语句。
    5.  责任分担:每个类只需要处理自己的工作即可,不能处理的传递给下一个对象完成,明确各类的责任,符合类的“单一职责原则”。

     缺点:

    1.  不能保证每一个请求都能被处理,由于每一个请求都没有明确的接收者,该请求可能一直传到链的末端都不能被处理。
    2.  对于比较长的责任链,请求的处理将涉及到多个处理对象,会影响系统的性能。
    3.  职责链的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误而导致系统出错,例如:可能会造成循环调用。

    4.4 使用场景

    1.  多个对象同时处理一个请求,但具体由哪个对象处理则在运行时动态决定。
    2.  在不明确接收者的情况下,向多个处理对象中的一个提交一个请求。
    3.  可动态执行一组对象处理请求。

    5 状态模式

    5.1 定义

         对有状态的对象,把复杂的“逻辑判断”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

    5.2 结构

    • 环境角色:也称为上下文,定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
    • 抽象状态角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
    • 具体状态角色:实现抽象状态所定义的行为。

    5.3 优缺点

     优点

    1.  便于扩展:将所有的与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
    2.  替换条件语句:允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

     缺点

    1. 必然会增加系统的类和对象的个数。
    2.  结构和实现都较为复杂,如果使用不当,将导致程序结构和代码的混乱。
    3.  “开闭原则”的支持不太好。

    5.4 使用场景

    1.  当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
    2.  一个操作中含有庞大的分支结构,并且这些分支决定对象的状态时,可以考虑使用状态模式。

    6 观察者模式

    6.1 定义

       又被称为发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象,当这个主题对象发生变化时,会通知所有的观察者对象,使它们能够自动地更新自己。

    6.2 结构

    •  抽象主题角色:把所有的观察者保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题值提供一个接口,可以增加或者删除观察者对象。
    •  具体主题角色:将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
    •  抽象观察者角色:是观察者的抽象类,定义了一个更新接口,以便在主题更改时更新自己。
    •  具体观察者角色:实现抽象观察者定义的更新接口,以便在主题更改时更新自己的状态。

    6.3 优缺点

     优点

    1.  降低耦合度:降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
    2.  实现广播机制:当目标发送通知后,所有注册的观察者都会收到消息。

     缺点

    1.  通知延迟:当观察者较多时,那么所有的观察者不能第一时间收到通知,有快的有慢的。
    2.  可能导致系统崩溃:如果目标有循环依赖的话,那么目标发送通知会使观察者循环调用,会导致系统崩溃。

    6.4 使用场景

    1.  对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
    2.  当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,使用观察者模式。

    7 中介者模式

    7.1 定义

    又叫调停模式,定义一个中介角色来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。

    7.2 结构

    •  抽象中介者角色:是中介者的接口,提供了同时对象注册与转发同时对象信息的抽象方法。
    • 具体中介者角色:实现中介者接口,定义一个List来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
    • 抽象同事类角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
    •  具体同事类角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

    7.3 优缺点

     优点

    1.  松散耦合:通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松耦合,基本上可以做到互补依赖,这样一来,同事对象就可以独立地变化和复用,不用再像以前那样“牵一发而动全身”了。
    2.  集中控制交互:多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了,当然如果是已经做好的系统,那么扩展中介者对象,而各个同事类不需要做修改。
    3.  一对多关联转为一对一关联:没使用中介者模式的时候,同事对象之间的关系通常是一对多的,引入中介者模式后,中介者对象和同事对象的关系通常变成双向的一对一,这会让对象的关系更容易理解和实现。

     缺点

       当同事类太多时,中介者的职责将很大,它会编的复杂而庞大,以至于系统难以维护。

    7.4 使用场景

    1.  系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解。
    2.  当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

    8 迭代器模式

    8.1 定义

    提供一个对象来顺序访问聚合对象中的一系列数据,而不是暴露聚合对象的内部显示。

    8.2 结构

    • 抽象聚合角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
    •  具体聚合角色:实现抽象聚合类,返回一个具体迭代器的实例。
    •  抽象迭代器角色:定义访问和遍历聚合元素的接口,通常包含hasNext()next()等方法。
    • 具体迭代器角色:实现抽象迭代器接口中所定义的方法,完成聚合对象的遍历,记录遍历的当前位置。

    8.3 优缺点

     优点

    1.  支持多种遍历:支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。
    2.  简化了聚合类:由于引入了迭代器,在原有的聚合对象中不需要自行提供数据遍历等方法,这样可以简化聚合类的设计。
    3.  在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无需修改原有代码,满足“开闭原则”的要求。

     缺点

       增加了类的个数,在一定程度上增加了系统的复杂性。

    8.4 使用场景

    1. 当需要为聚合对象提供多种遍历方式时。
    2.  当需要为遍历不同的聚合结构提供一个统一的接口时。
    3. 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。

    9 访问者模式

    9.1 定义

    封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下,定义作用于这些元素的新的操作。

    9.2 结构

    •  抽象访问者角色:定义了对每一个元素访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变。
    •  具体访问者角色:给出每一个对元素类访问时所产生的具体行为。
    •  抽象元素角色:定义了一个接受访问者的方法,其意义是指,每一个元素都要可以被访问者访问。
    • 具体元素角色:提供接受访问方法的具体实现,而这个具体的实现,通常情况下,是使用访问者提供的访问该元素类的方法。
    •  对象结构角色:定义当中所提到的对象结构,对象结构是一个抽象标书,具体点可以理解为一个具有容器性质或者符合对象特性的类,它会含有一组元素,并且可以迭代这些元素,供访问者访问。

    9.3 优缺点

     优点

    1.  扩展性好:在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
    2.  复用性好:通过访问者来定义整个对象结构通用的功能,从而提高复用程度。
    3.  分离无关行为:通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。

     缺点

    1.  对象结构变化很困难:在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
    2.  违反了依赖倒置原则:依赖了具体类,而没有依赖抽象类。

    9.4 使用场景

    1. 对象结构相对稳定,但其操作算法经常变化的程序。
    2. 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。

    10 备忘录模式

    10.1 定义

    又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时,能将该对象恢复到原先保存的状态。

    10.2 结构

    • 发起人角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里所有信息。
    •  备忘录角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
    •  管理者模式:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

    备忘录有两个接口:窄接口、宽接口。

    10.3 优缺点

     优点

    1.  提供了一种可以恢复状态的机制,当用户需要时能够比较方便地将数据恢复到某个历史的状态。
    2.  实现了内部状态的封装,除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
    3.  简化了发起人类:发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合“单一职责原则”。

     缺点

       资源消耗大:如果要保存的内部状态过多或者特别频繁,将会占用比较大的内存资源。

    10.4 使用场景

    1.  需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
    2.  需要提供一个可回滚操作的场景,如:word、记事本、数据库中的事务操作。

    11 解释器模式

    11.1 定义

     定义语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”意思是使用规定格式和语法的代码,它是一种类行为型模式。

    11.2 结构

    • 抽象表达式角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法interpret()
    •  终结符表达式角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
    •  非终结符表达式角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
    •  环境角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
    • 客户端:主要任务是将需要分析的句子或表达式转换成使用的解释器对象苗顺的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

    11.3 优缺点

     优点

    1.  易于改变和扩展文法:由于在解释器模式中使用类来表示语言的文法规则,因此可通过继承等机制来改变或扩展文法,每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
    2.  实现文法较为容易:在抽象语法树种每一个表达式结点类的实现方式都是相似的,这些类的代码编写都不会特别复杂。
    3. 增加新的解释表达式较为方便:如果用户需要增加新的解释表达式,只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类diamante无需修改,符合“开闭原则”。

     缺点

       对于复杂文法难以维护:在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护。

     执行效率较低:由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

    11.4 使用场景

    1.  当语言的文法较为简单,且执行效率不是关键问题时,使用解释器模式。
    2.  当问题重复出现,且可以用一种简单的语言来进行表达式,使用解释器模式。
    3.  当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,使用解释器模式。
  • 相关阅读:
    分布式缓存技术之Redis_03分布式redis
    Spring 二、400行代码手写初体验Spring V1.0版本
    Spring 一、各级架构与依赖关系
    Java正则表达式基础学习
    JAVA开发:SpringBoot多数据源配置
    Spring 单例模式实现源码分析
    Spring 使用的设计模式用哪些
    Spring之@Autowired和@Resource
    Spring的优缺点
    MySQL支持的事物隔离级别以及悲观锁和乐观锁原理和应用场景
  • 原文地址:https://www.cnblogs.com/shuliqiu0320/p/14478064.html
Copyright © 2011-2022 走看看