认认真真学习设计模式

  23中设计模式,于我的印象一直是高深莫测,就像一座很难翻越的大山。现在下定决心,不仅要翻越过去,还要是脚踏实地的一步一步走过去的,彻底弄通设计模式,虽说不求融会贯通、灵活运用(没有实战环境,很难做到),但一定要彻底理解。

学习路线:

  先看视频,再看《HeadFirst设计模式》

什么是模式:

  模:就是模型、模板

  式:就是方式、方法

模式:

  就是能够当作模型或模板的方式方法。

设计模式:

  在设计中可以当作模型或模板的设计方式方法,即所谓的解决方案。

分类:

  创建模式:解决使用过程如何动态创建不同对象,比如:建造模式。

  结构模式:专注于解决结构化的划分、设计

  行为模式:专注于设计时我们的行为动作,怎样使我们的设计更加灵活、可扩展。

优点:

  规范开发,面向接口编程

  降低耦合度

  增加灵活性

总体来说设计模式分为三大类:

  创建模式包括:工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)、单例模式、建造模式、原始模型模式等。

  结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

 ----------------创建模式----------------

创建模式:对类的实例化过程的抽象化。一些系统在创建对象时,需要动态的决定怎样创建对象,创建哪些对象,以及如何组合和表示这些对象。创建模式描述了怎样构造和封装这些动态的决定。

 工厂模式:

  简单工厂模式(即静态工厂方法模式)

  工厂方法模式

  抽象工厂模式

一、简单工厂模式

简单工厂模式类图:

简单工厂模式的角色:

  工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中他主要有一个具体的类。

  抽象产品角色:它一般是具体产品继承的父类或实现的接口。在java中由接口或抽象类实现。

  具体产品角色:工厂类所创建的对象就是此角色的实例。

优点:

  客户类和工厂类分开。工厂类关注于产品的生产。客户端“消费者”任何时候需要某种产品,只需向工厂请求即可,可以无需知道每次实例化哪个类。消费者无需修改就可以接纳新产品。

缺点:

  当产品修改或有新产品加入时,工厂类也要做相应的修改。

二、工厂方法模式

  定义一个创建产品的工厂接口或抽象类。核心工厂不再负责所有产品的创建。而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

工厂方法模式类图:

角色:

  抽象工厂角色:这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。

  具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。

  抽象产品角色:它是具体产品继承的父类或抽象的接口。

  具体产品角色:具体工厂角色创建的对象就是此角色的实例。

  每一个具体工厂对应一个具体产品的创建

优点:

  这种工厂方法模式可以在不修改具体工厂角色的情况下,增加新的产品。

三、抽象工厂模式

  抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。抽象工厂模式的用意为:给客户端提供一个接口或抽象类,可以创建多个产品族中的产品对象,而且使用抽象工厂模式还需满足以下条件:

  1)系统中有多个产品族

  2)系统一次只能消费其中一族产品及其使用。

产品组:

  位于不同产品等级结构中,功能相关联的产品组成的家族。

类图:

角色:

  抽象工厂角色:这是抽象工厂模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。

  具体工厂角色:它含有和具体业务逻辑相关的代码。由应用程序调用以创建对应的具体产品的对象。

  抽象产品角色:它是具体产品继承的父类或者是实现的接口。

  具体产品角色:具体工厂角色所创建的对象就是此角色的实例。

四、建造者模式

  将产品的内部表象和产品的生成过程分割出来。从而使一个建造过程生成具有不同的内部表象的产品对象。建造者模式使得产品内部表象可以独立的变化。客户不必知道产品内部组成的细节。

  建造者模式可以强制执行一种分步骤进行的建造过程。

与抽象工厂模式的区别:

  在建造者模式里,有个指导者,由指导者来管理建造者。用户是与指导者联系的,指导者联系建造者最后得到产品;即建造者模式可以强制实行一种分步骤进行的建造过程。

类图:

角色:

  抽象建造者角色:规范产品对象的各个组成部分的建造,只负责定制规范。

  具体建造者角色:是与应用程序紧密相关的类,在指导者的调用下创建产品的实例。这角色实现抽象建造者,完成产品的组装。

  指导者角色:调用具体建造者角色以完成创建产品。指导者不需要产品类的具体实现。产品的具体实现在具体建造者对象。举例:此模式应用的场景是产品有多个部分组成,每个具体的工厂建造出来的各个部分是不同的,而指导者角色只是可以规定各个具体建造工厂建造的顺序。

  产品角色:产品类、组件类,包括产品的接口或抽象类和实现类。

应用场景:

  一个产品有很多个组成部分,我们必须能够将各个部分一一去实现,并且按照一定的顺序去组装产品。在组装过程中,我们可以动态决定内部表象(内部组成部分,例如Person里的 Head,body)的不同,这个过程可以通过指导者指导我们创建对象的内部表象,并且能够实现其内部表象的变化。

五、原始模型模式

  通过给出一个原型对象来指明所要创建的对象的类型。然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式可以动态的增加或者减少产品类,产品类不需要非得有任何事先确定的等级结构,原始原型模式适合于任何等级结构。

类图:

角色:

  客户角色:完成一个原始对象来克隆自己得到一个新对象。

  抽象原型角色:实现自己的clone方法,通常是抽象类,且具有许多具体的子类。

  具体原型角色:被复制的对象,为抽象类的具体子类。

总结:

  原型模式使用clone能够动态的抽取当前对象运行时的状态并且克隆到新的对象中,新对象就可以在此基础上进行操作而不损坏原有对象。而new只能得到一个刚初始化的对象,而在实际应用中,往往是不够的。

缺点:

  是每一个类都必须配备一个克隆方法。

  由于clone方法在java实现中有一定的弊端和风险。所以clone方法是不建议使用的。因此很少能在java应用中看到原型模式的使用。

六、单例模式

  单例模式又叫做单态模式或者单件模式

单例模式有以下特点:

  (1)单例类只能有一个实例

  (2)必须自己创建自己的唯一实例。

  (3)必须对外提供这一实例

  单例模式只应在有真正的“单一实例”的需求时才可使用。

类图:

使用场景:

  涉及不到和其他类之间的关系,而是说某一个类符合单例模式。

----------------结构模式----------------

七、适配器模式

  把一个类的接口转换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类能够一起工作。

适配器模式的两种形式:

1.类的适配器模式

 把适配的类的API转换为目标的API。静态类图如下:

 该模式涉及到角色:

   1)目标(target)角色:所期待的得到的接口

   2)源(Adaptee)角色:需要适配的类

   3)适配器(Adapter)角色:把源角色转化为目标接口,核心类。

2.对象的适配器模式

  与类的适配器模式一样,对象适配器模式把被适配的类的API转换为目标类的API,不同的是,对象适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类。类图如下:

  模式的角色:

    1)目标(target)角色:客户端期待使用的接口或类。

    2)源(Adaptee)角色:所需要适配的接口或类

    3)适配器(Adapter)角色:把源角色转化为目标接口的核心类。

缺省适配器模式

  为一个接口提供缺省实现,这样子类型可以从这个缺省实现进行扩展,而不必从原有的接口进行扩展。通常这个缺省实现类可以是一个抽象类。

好处:

  很多时候一个接口的扩展类不需要实现所有的方法,而是需要某一个两个方法即可,从原接口扩展必须实现所有方法,而从该缺省实现类扩展只需要重写需要的方法即可。

八、桥梁模式

  将抽象化与实现化脱藕,使得二者可以独立的变化,也就是说将它们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

类图:

涉及角色:

  (1)抽象化(Abstraction)角色:抽象化类的定义,并包含一个实现化对象的引用(也就是组合/聚合关系)。

  (2)修正抽象化(Refined Abstraction)角色:拓展抽象化角色类,改变和修正父类对抽象化的定义。

  (3)实现化(Implementor)角色:实现化角色的接口或抽象类,这个接口不一定和抽象化角色定义相同。

  (4)具体实现化(Concrete Implementor)角色:实现化角色接口或者抽象类的具体实现。

个人理解:

  桥梁模式,顾名思义,就是在抽象化角色与实现化角色之间搭建一座桥梁,从而将抽象化与实现化脱藕。

  例如:咖啡案例。咖啡是抽象化角色。如果想泡制大杯加奶咖啡,按照以前的方式,就需要新建一个大杯加奶咖啡具体类来实现咖啡抽象类,再如果想泡制中杯不加奶咖啡,就需要新建一个中杯不加奶具体类来实现咖啡抽象类。而如果使用桥梁模式,则在 咖啡抽象类 和 具体咖啡实现类 之间搭建一座桥梁,这个桥梁就是 抽象化角色与抽象化咖啡实现类(实现化角色,可以是接口或者抽象类)之间的关系,具体咖啡实现类(加奶、不加奶)通过实现 抽象化咖啡实现类 即可,这样的话,就将 抽象化角色 与 实现化角色 脱藕。而通过继承 咖啡抽象类 来修正 抽象化角色(大杯、中杯)。从而使系统更加灵活,只需要组合就行,减少了需要编写的类的数量。

九、合成模式(Composite)

  将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构模式。合成模式把部分与整体的关系用树结构表示出来。

  合称模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。

合成模式类图:

三个角色:

  (1)抽象构件(Component)角色:组合对象统一的规定接口或抽象类。

  (2)树叶构件(Leaf)角色:参加组合的树叶对象,没有下级对象。

  (3)树枝构件(Composite)角色:参加组合的子对象,可以具有下级子对象。

合成模式分类:

  合成模式可以不提供父对象的管理方法,但需要在合适的地方提供子对象的管理方法,诸如add(..)、remove(..)以及getChild(..)等方法。

基于这方面的考虑,合成模式分为透明方式和安全方式两类。

  (1)透明方式:在Component里面声明所有用来管理子类对象的方法,包括add(..)、remove(..)以及getChild(..)等方法。

  类图:

  优点:

  所有构件类都有相同的接口,树枝和树叶在功能上都是相同透明的。

  缺点:

  不安全,树叶类对象应该没有下级对象,这些管理方法也没有意义,但在编译时不错,运行时可能会出错。

  (2)安全方式:在Composite类里面声明管理子类对象的方法。这样做法比较安全。树叶类型的对象没有管理子类对象的方法。客户端使用叶子节点对象时,编译就会检查管理方法使用的错误。

 类图:

  应用场景:比如 导航,可以考虑根据合成模式进行设计

十、装饰模式(又叫包装器Wrapper模式)

  动态给对象增加大量的功能。以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤销。增加由一些基本功能的排列组合而产生的非常大量的功能。

 类图;

 角色:

  1)抽象构件(Component)角色:抽象类或接口,以规范准备接受附加责任的对象。

  2)具体构件(Concrete Component)角色:抽象构件的具体实现类。

  3)装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个和抽象构件一样的接口(继承);

  4)具体装饰(Concrete Decorator)角色:装饰角色的子类,负责给构件对象“贴上”附加的责任。

好处:

  使用过程中给对象动态增加功能。

为什么使用Decorator?

  我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须预见这些拓展功能(这些功能是在哪个类里),这些功能是编译时就确定了,是静态的。

  利用装饰模式会更加灵活。这些功能需要由用户动态决定加入的方式和时机,Decorator提供了“即插即用”的方法,在运行期间决定何时增加何种功能。

十一、门面模式

  外部与一个子系统的的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用,每一个子系统只有一个门面类,但整个系统可以有多个门面类。

  是对于层次结构的设计。

门面模式角色:

  1)门面(Facade)角色:客户端可以调用这个角色的方法。此角色知晓相关子系统的功能和责任。会将所有从客户端发来的请求委派到相应的子系统中去。

  2)子系统(Subsystem)角色:可以同时有一个或多个子系统。每个子系统都不是单独的类。而是一个类的集合。每个子系统都可以被客户端调用或者门面角色调用。子系统不知道门面角色的存在。对于子系统而言,门面仅仅是另外一个客户端而已。

适用性:

  1.为一个复杂子系统提供一个简单接口。

  2.提高系统的独立性。

  3.在层次化结构中,可以使用Facade模式定义系统中每一层的入口。

Facade设计模式更注重从架构的层次去看整个系统,而不是单个类的层次。Facade很多时候更是一种架构设计模式。

十二、享元模式

  FLYWEIGHT在拳击比赛中指轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。

  它所达到的目的就是如何让细粒度对象共享。开发过程中,有许多对象需要共享使用,需要轻量级设计。

  享元模式能做到共享的关键是区分内蕴状态和外蕴状态。

内蕴状态:

  是存储在享元对象内部的,并且是不会随环境的改变而有所不同。因此,一个享元可以具有内蕴状态并可以共享。实际上就是 某个类内部的属性。

外蕴状态:

  是随环境的改变而改变的、不可以共享的。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态不可以影响享元对象的内蕴状态,它们是相互独立的。实际上就是 对象调用其某个方法时传入的参数。

  享元模式可以分为单纯享元模式和复合享元模式两种形式。

  复合享元模式是单纯享元模式和合成模式的组合。

单纯享元模式类图:

单纯享元模式角色:

  1)抽象享元(Flyweight)角色:给出一个接口,规定享元需要实现的方法。

  2)具体享元(Concrete Flyweight)角色:实现享元角色规定的接口。如果有内蕴状态,必须提供存储空间(数据或字段)。

  3)享元工厂(Flyweight Factory)角色:负责创建和管理享元对象。本角色必须保证享元对象可以被系统适当的共享。如果有了合适的享元对象,就使用已有的;如果系统中没有合适的享元对象,就应该创建一个合适的享元对象。

十三、代理模式(proxy)

  给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或另一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。

  如果真实对象功能比较弱,可以在代理对象里增加其他的功能。

类图:

代理模式的角色:

  1)抽象主题角色:声明真实主题和代理主题的共同接口。他们类型统一,真实主题就可以被代理主题所代替。

  2)代理主题角色:内部包含对真实主题的引用,并且实现和真实主题角色相同的接口。

  3)真实主题角色:定义真实的对象类。

静态代理:一个代理类只能代理一个类型

动态代理:

  java在JDK1.3之后加入协助开发动态代理功能的类。我们不必为特定对象与方法写特定的代理。使用动态代理,可以使得一个handler服务于各个对象,首先,一个handler必须实现java.lang.reflect.InvocationHandler。

  代理工厂一定关注于一件事情,对被代理对象增强的事情。

  代理对象和被代理对象实现了同一个接口。

  由代理工厂批量生产一类代理对象。

 ----------------行为模式--------------------

十四、责任链模式(Chain of Responsibility)

  在责任链模式里,有很多对象组成一条链,每个对象持有下个对象的引用。请求在这个链上传递。直到链上的某个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理此请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。

  处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接受端对象所接受。

责任:就是处理需求,处理请求。

类图:

角色:

  1)抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义出一个方法以设定和返回对下家的引用。这个角色通常由一个java抽象类或者java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handlerRequest()规范了子类处理请求的操作。

  2)具体处理者(ConcreteHandler)角色:具体处理者接收到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。

十五、命令模式(command)

  把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接受请求一方的接口,更不必知道请求是怎么被接受,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤销。

结构图:

角色:

  1)客户(Client)角色:创建一个具体的命令(ConcreteCommand)对象并确定其接受者。

  2)命令(Command)角色:声明一个给所有具体命令类的抽象接口。

  3)具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合:实现execute方法,负责调用接受者的相应操作。

  4)请求者(Invoker)角色:负责调用命令对象执行请求。

  5)接受者(Receiver)角色:负责具体实施和执行一个请求。

命令模式的优点:

  1:更松散的耦合。请求者(Invoker)不直接与接受者(Receiver)交互。

  2:更动态的控制。

  3:能很自然的复合命令。

  4:更好的扩展。

  在命令模式中,请求者(Invoker)不直接与接受者(Receiver)交互,即请求者(Invoker)不包含接受者(Receiver)的引用,因此彻底消除了彼此之间的耦合。

如果增加新的具体命令和该命令的接受者,不必修改调用者的代码,调用者就可以使用新的命令对象;反之,如果增加新的调用者,不必修改现有的具体命令和接受者,新增加的调用者就可以使用已有的具体命令。

十六、解释器模式

  给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子中。

解释器结构图:上下文环境  抽象表达式  终结表达式  非终结表达式

解释器模式角色:

  1)抽象表达式角色:声明一个抽象的解释操作,这个接口为所有具体表达式角色(抽象语法树中的节点)都要实现的。

  2)终结符表达式角色:具体表达式

  3)非终结符表达式角色:具体表达式

  4)上下文(环境)角色:包含解释器之外的一些全局信息。

  5)客户角色:

    a)构建(或者被给定)表示该文发定义的语言中的一个特定的句子的抽象语法树

    b)调用解释操作。

十七、迭代器模式(Iterator)

  可以顺序访问一个聚集内的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代器模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身个隔开。迭代模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代对象,每一个迭代的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。

迭代器模式结构图:

 角色:

  1)迭代器角色(Iterator):负责定义访问和遍历元素的接口。

  2)具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。

  3)容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。

  4)具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口--这个具体迭代器角色与该容器的结构相关。

十八、调停者模式(中介者模式 mediator)

      包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散耦合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。

调停者模式结构图:

调停者模式角色:

  1)抽象调停者(Mediator)角色:抽象调停者角色定义统一的接口用于各同事角色之间的通信。

  2)具体调停者(Concrete Mediator)角色:具体调停者角色通过协调各同事角色实现协作行为。为此他要知道并引用各个同事角色。

  3)抽象同事(Colleague)角色:定义出调停者到同事对象的接口或者抽象类。

  4)具体同事(ConcreteColleague)角色:每一个同事角色都知道对应的具体调停者角色,而且与其他同事角色通信的时候,一定要通过调停者角色协作。

调停者模式的优点:

  1.松散耦合

   调停者模式通过把多个同事对象之间的交互封装到调停者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互不依赖。这样一来,同事对象就可以独立的变化和复用,而不再像以前那样“牵一处而动全身了”。

  2.集中控制交互

    多个同事对象的交互,被封装在调停者对象里面集中管理。使得这些交互行为发生变化的时候,只需要修改调停者对象就可以了。当然如果是已经做好的系统,那么就扩展调停者对象,而各个同事类不需要做修改。

  3.多对多变成一对多

   没有使用调停者模式的时候,同事对象之间的关系通常是多对多的,引入调停者对象后,调停者对象和同事对象的关系通常变为双向的一对多,这会让对象的关系更容易理解和实现。

调停者模式的缺点:

  调停者模式的一个潜在的缺点是,过度的集中化。如果同事对象的交互非常多,而且比较复杂,当这些复杂性全部集中到调停者的时候,这会导致调停者对象变得十分复杂,而且难于管理和维护。

十九、备忘录模式(又叫快照模式   memento)

  备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

  将一个对象的内部状态放入备忘录对象中,再将此备忘录对象存储在负责人角色中。恢复状态的时候,先把备忘录对象从负责人角色中取出来,在把内部状态从备忘录对象中取出来。

备忘录模式结构图:

角色:

  1.备忘录(Memento)角色:将发起人(Originator)对象的内部状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态。

  2.发起人(Originator)角色:

    (1)创建一个含有当前内部状态的备忘录对象。

    (2)使用备忘录对象存储器内部状态。

  3.负责人(Caretaker)对象:

    (1)负责保存备忘录对象。

    (2)不检查备忘录对象的内容。

二十、观察者模式

   又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

   定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,是他们能够自动更新自己。

观察者模式结构图:

角色:

  1)抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫抽象被观察者(Observable)角色。

  2)具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫具体被观察者(Concrete Observable)角色。

  3)抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。

  4)具体观察者(ConcreteObserver)角色:存储与主题的状态相应的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

二十一、状态模式(state)

  允许一个对象在其内部状态改变的时候改变行为。这个对象看上去像是改变了它的类一样。

  状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。

  状态模式的意图是让一个对象在其内部状态改变时,其行为也随之改变。

状态模式结构图:

 角色:

  1)环境(Context)角色:也叫上下文,定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。

  2)抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。

  3)具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。

 从上面可以看出:

  环境类Context的行为request()是委派给某一个具体状态类的。通过使用多态性原则,可以动态改变环境类Context的属性State的内容,使其从指向一个具体状态类变换到指向另一个具体状态类,从而使环境类的行为request()由不同的具体状态类来执行。

优点:

  1.状态模式将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。

  2.所有状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类很容易地增加新的状态和转换。

  3.状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。

缺点:

  导致较多的ConcreteState子类。

适用场景:

  1.当一个对象的行为诶取决于它的状态时,并且它必须在运行时刻根据状态改变他的行为时,就可以考虑使用状态模式。

  2.一个操作中含有庞大的分支机构,并且这些分支决定于对象的状态。

二十二、策略模式(strategy)

  针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得他们可以相互替换。

  策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类(即策略类),各种算法在具体的策略类中提供。

  由于算法和环境独立开来,算法的增减、修改都不会影响到环境和客户端。

策略模式结构图:

 角色:

  1)算法使用环境(Context)角色;算法被引用到这里和一些其它的与环境有关的操作一起来完成任务。

  2)抽象策略(Strategy)角色:规定了所有具体策略角色所需的接口。在java它通常由接口或者抽象类来实现。

  3)具体策略(Concrete Strategy)角色:实现了抽象策略角色定义的接口。

 应用场景:

  1.多个类只区别在表现行为(算法)不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。

  2.需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其他方式实现。

  3.对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

优点:

  1.提供了一种替代继承的方法,而且既保持了继承的优点(代码重用),还比继承更灵活(算法独立,可以任意扩展)。

  2.避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。

缺点:

  客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。

二十三、模板方法模式(Template method)

  准备一个抽象类,在一个模板方法中定义一个算法的骨架,而将一些步骤实现延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。将主要的方法定义为final,防止子类修改算法骨架,将子类必须实现的方法定义为abstract。而普通的方法(无final或abstract修饰)称之为钩子方法。

  钩子方法作用:

  1)作为可选内容,子类可以重写或者置之不理。

  2)  让子类有机会对模板方法中即将发生的或者已经发生的步骤做出反应。

  3)  作为控制条件,使得子类可以影响到抽象类中算法流程。

  模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。

模板方法模式结构图:

 角色:

1)抽象模板(Abstract Template)角色:

  (1)定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。

  (2)定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。

2)具体模板(Concrete Template)角色有如下责任:

  (1)实现父类所定义的一个或多个抽象方法,他们是一个顶级逻辑的组成步骤。

  (2)每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(或是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。

优点:

  1)模板方法模式在一个类中形式化地定义算法,而由它的子类实现细节的处理。

  2)模板方法是一种代码复用的基本技术,它提取了类库中的公共行为。

  3)模板方法模式导致一种反向的控制结构,这种结构有时被称为“好莱坞法则”,即“别找我们,我们找你”,通过一个父类调用其子类的操作(而不是相反的子类调用父类),通过对子类的扩展增加新的行为,符合“开闭原则”。

缺点:

  每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高。

二十四、访问者模式(visitor)

  访问者Visitor表示一个作用于某对象结构中各元素的操作。访问者模式可以使不修改各元素类的前提下定义作用于这些元素的新操作,也就是动态的增加新的状态操作方法。

  Visitor模式是一种分离对象数据结构与行为的方法,通过这种分离,可以为一个已存在的类或类群增加新的操作而无需为他们做任何修改。

访问者模式结构类图:

访问者模式角色:

  1)访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口。

  2)具体访问者角色(ConcreteVisitor):实现每个由访问者角色(Visitor)声明的操作。

  3)元素角色(Element):定义一个Accept操作,他以一个访问者为参数。

  4)具体元素角色(ConcreteElement):实现由元素角色提供的Accept操作。

  5)对象结构角色(ObjectStructure):这是使用访问者模式必备的角色。他要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。

访问者模式的优点:

  1)好的扩展性:能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。

  2)好的复用性:可以通过访问者来定义整个对象结构通用的功能,从而提高复用程度。

  3)分离无关行为:可以通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单

访问者模式的缺点:

  对象结构变化很困难:不适用于对象结构中的类经常变化的情况,因为对象结构发生了改变,访问者的接口和访问者的实现都要发生相应的改变,代价太高。

  赶在过年之前,设计模式终于大结局了,共花费了大概两个多月的时间,其实一个多月的时候就差不多已经还剩下三四个设计模式了,但由于公司实行起了996模式,所以一直没有时间学习,就一直拖到了现在。学的过程中,发现好的学习视频对于学习技术来说特别重要,以后学习一些比较难的技术点的时候,可以先看看视频,再边看书边实践。

原文地址:https://www.cnblogs.com/fengbing9891/p/6033178.html