常用设计模式总结

对上一篇的设计模式总结。个人见解不喜勿喷


一、策略模式和模板方法模式


Strategy模式允许外界使用其接口方法,因而可以将这个接口方法认为是"一整个算法";
Template Method模式可以限制所留下的虚方法只对其继承类可见,外部使用者不一定能够直接使用这些虚方法,因而可以将这些虚方法认为是"一个算法的一部分".


如果我们要封装的算法适合于提供给用户任意使用,是"一整个算法",那么用Strategy模式较好;
如果要封装的变化是一个算法中的部分(换言之,大算法的步骤是固定的),而且我们不希望用户直接使用这些方法,那么应该使用Template Method模式.


模板模式和策略模式通常可以互相替换。它们都像试卷,模板模式是填空题,策略模式是选择题。


1.模板方法模式


两个角色:
(1) AbstractClass(抽象类):在抽象类中定义了一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,
每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),
用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。
(2) ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。


模板方法:
一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法。这个模板方法定义在抽象类中,并由子类不加以修改地完全继承下来。
模板方法是一个具体方法,它给出了一个顶层逻辑框架,而逻辑的组成步骤在抽象类中可以是具体方法,也可以是抽象方法。
由于模板方法是具体方法,因此模板方法模式中的抽象层只能是抽象类,而不是接口。


基本方法:
基本方法是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。
(1) 抽象方法:一个抽象方法由抽象类声明、由其具体子类实现。在C#语言里一个抽象方法以abstract关键字标识。
(2) 具体方法:一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
(3) 钩子方法:一个钩子方法由一个抽象类或具体类声明并实现,而其子类可能会加以扩展。
通常在父类中给出的实现是一个空实现(可使用virtual关键字将其定义为虚函数),并以该空实现作为方法的默认实现,当然钩子方法也可以提供一个非空的默认实现。




2.策略模式
抽象策略角色:策略类,通常由一个接口或者抽象类实现。
具体策略角色:包装了相关的算法和行为。
环境角色:持有一个策略类的引用,最终给客户端调用。


策略模式的重心:
策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。


算法的平等性:
策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法,大家的地位是完全一样的,正因为这个平等性,才能实现算法之间可以相互替换。
所有的策略算法在实现上也是相互独立的,相互之间是没有依赖的。
策略算法是相同行为的不同实现。


运行时策略的唯一性:
运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。


公有的行为:
经常见到的是,所有的具体策略类都有一些公有的行为。这时候,就应当把这些公有的行为放到共同的抽象策略角色Strategy类里面。当然这时候抽象策略角色必须要用Java抽象类实现
,而不能使用接口。


优点:
策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句
里面,比使用继承的办法还要原始和落后。


缺点:
客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算
法或行为的情况。
由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。


应用场景:算法,折扣活动(对初级会员没有折扣,对中级会员提供10%的促销折扣,对高级会员提供20%的促销折扣)
快速排序、冒泡排序、选择排序




二、策略模式(模板方法模式)与工厂模式


在模式结构上,两者很相似


1.用途不一样 
工厂是创建型模式,它的作用就是创建对象; 
策略是行为型模式,它的作用是让一个对象在许多行为中选择一种行为;


2.关注点不一样 
一个关注对象创建 
一个关注行为的封装


3.解决不同的问题 


工厂模式是创建型的设计模式,它接受指令,创建出符合要求的实例;它主要解决的是资源的统一分发,将对象的创建完全独立出来,让对象的创建和具体的使用客户无关。主要应用在多
数据库选择,类库文件加载等。 
策略模式是为了解决的是策略的切换与扩展,更简洁的说是定义策略族,分别封装起来,让他们之间可以相互替换,策略模式让策略的变化独立于使用策略的客户。


4.工厂相当于黑盒子,策略相当于白盒子


工厂模式:
有一天你决定去吃披萨,一看菜单,哦,种类很多呀,你就点了个培根披萨,过了二十分钟,你的披萨就来了就可以吃到了。
但这个披萨是怎么做的,到底面粉放了多少,培根放了多少,佐料放了多少,有多少到工序,你是不需要管的,你需要的是一个美味培根披萨。
策略模式:
同样还是在披萨店,你要一个培根披萨,老板说想吃自己去做吧。原料有培根、面粉、佐料。工序有1、2、3工序,你自己去做吧。
然后你就需要自己去做,到底放多少培根,放多少面粉,放多少佐料,这都你自己来决定,工序1、2、3,你是怎么实现的,都你自己决定。最后你得到了披萨。




三、简单工厂、工厂方法、抽象工厂、建造者模式


1.简单工厂
简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。
不修改代码的话,是无法扩展的。


2.工厂方法
工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。
在同一等级结构中,支持增加任意产品。


3.抽象工厂
抽象工厂是应对产品族概念的。比如说,每个汽车公司可能要同时生产轿车,货车,客车,那么每一个工厂都要有创建轿车,货车和客车的方法。
应对产品族概念而生,增加新的产品线很容易,但是无法增加新的产品。


4.建造者模式
创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是对象的内部组成构件面临着复杂的变化。
JavaMail使用到了该模式。




四、适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式


1、适配器模式
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。


分类:
类的适配器模式、对象的适配器模式、接口的适配器模式


优点:
通过适配器,客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单、更直接、更紧凑。
复用了现存的类,解决了现存类和复用环境要求不一致的问题。
将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。


缺点:
对于对象适配器来说,更换适配器的实现过程比较复杂。


适用场景:
系统需要使用现有的类,而这些类的接口不符合系统的接口。
想要建立一个可以重用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
两个类所做的事情相同或相似,但是具有不同接口的时候。
旧的系统开发的类已经实现了一些功能,但是客户端却只能以另外接口的形式访问,但我们不希望手动更改原有类的时候。
使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能。




2、装饰模式和代理模式(构造器不同)
装饰模式:以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案;
代理模式:给一个对象提供一个代理对象,并由代理对象来控制对原有对象的引用;


对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。
对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。
此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。


装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。
用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。
因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。
并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。


3、外观模式(Facade)
定义:为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。


通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。
外观模式中,一个子系统的外部与其内部的通信通过一个统一的外观类进行,外观类将客户类与子系统的内部复杂性分隔开,
使得客户类只需要与外观角色打交道,而不需要与子系统内部的很多对象打交道。


优点:
(1) 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易。通过引入外观模式,客户端代码将变得很简单,与之关联的对象也很少。
(2) 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。
(3) 一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。


缺点:
(1) 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活 性。
(2) 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则。


适用场景:首页或导航页面等
(1) 当要为访问一系列复杂的子系统提供一个简单入口时可以使用外观模式。
(2) 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性。
(3) 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。




4、桥接模式和策略模式、状态模式


(1)桥接模式
桥接模式的本质:分离抽象和实现。


JDBC、JPA、slf4J(slf4j nodep、log4j、jdk logging api、Apache commons-log)


桥接与接口:
接口直接使用接口对象,不像桥接模式的抽象部分那样,是持有具体实现部分的接口,这就导致画出来的结构图,一个是依赖,一个是聚合关联。
桥接模式中的抽象部分持有具体实现部分的接口,最终目的是什么,还不是需要通过调用具体实现部分的接口中的方法,来完成一定的功能,
这跟直接使用接口没有什么不同,只是表现形式有点不一样。


从广义桥接模式的角度来看,平日熟悉的三层架构其实就是在组合使用桥接模式。
桥接模式是可以连续组合使用的,一个桥接模式的实现部分,可以作为下一个桥接模式的抽象部分。
如果从更本质的角度来看,基本上只要是面向抽象编写的Java程序,都可以视为是桥接模式的应用,都是让抽象和实现相分离,从而使它们能独立的变化。
不过只要分离的目的达到了,叫不叫桥接模式就无所谓了。


优点:
分离抽象和实现部分:桥接模式分离了抽象和实现部分,从而极大地提高了系统的灵活性。让抽象部分和实现部分独立开来,分别定义接口,这有助于对系统进行分层,从而产生更
好的结构化的系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。
更好的扩展性:由于桥接模式把抽象和实现部分分离开了,而且分别定义接口,这就使得抽象部分和实现部分可以分别独立的扩展,而不会相互影响,从而大大的提高了系统的可扩展性。
可动态切换实现: 由于桥接模式把抽象和实现部分分离开了,那么在实现桥接的时候,就可以实现动态的选择和使用具体的实现,也就是说一个实现不再是固定的绑定在一个抽象.
接口上了,可以实现运行期间动态的切换实现。
可减少子类的个数:根据前面的讲述,对于有两个变化纬度的情况,如果采用继承的实现方式,大约需要两个纬度上的可变化数量的乘积个子类;而采用桥接模式来实现的话,大约
需要两个纬度上的可变化数量的和个子类。可以明显地减少子类的个数。


何时选用桥接模式:
如果你不希望在抽象和实现部分采用固定的绑定关系,可以采用桥接模式,来把抽象和实现部分分开,然后在程序运行期间来动态的设置抽象部分需要用到的具体的实现,还可以动
态切换具体的实现。
如果出现抽象部分和实现部分都应该可以扩展的情况,可以采用桥接模式,让抽象部分和实现部分可以独立的变化,从而可以灵活的进行单独扩展,而不是搅在一起,扩展一边会影
响到另一边。
如果希望实现部分的修改,不会对客户产生影响,可以采用桥接模式,客户是面向抽象的接口在运行,实现部分的修改,可以独立于抽象部分,也就不会对客户产生影响了,也可以
说对客户是透明的。
如果采用继承的实现方案,会导致产生很多子类,对于这种情况,可以考虑采用桥接模式,分析功能变化的原因,看看是否能分离成不同的纬度,然后通过桥接模式来分离它们,从
而减少子类的数目。


桥接模式与策略模式:
最主要的是模式的目的不一样,策略模式的目的是封装一系列的算法,使得这些算法可以相互替换;而桥接模式的目的是分离抽象和实现部分,使得它们可以独立的变化。


桥接模式和状态模式:
由于从模式结构上看,状态模式和策略模式是一样的,这两个模式的关系也基本上类似于桥接模式和策略模式的关系。
只不过状态模式的目的是封装状态对应的行为,并在内部状态改变的时候改变对象的行为。




5、组合模式
组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 "组合对象" 的含义。


组合模式可以让客户端像修改配置文件一样简单的完成本来需要流程控制语句来完成的功能。


组合模式让你可以优化处理递归或分级数据结构。有许多关于分级数据结构的例子,使得组合模式非常有用武之地。
关于分级数据结构的一个普遍性的例子是你每次使用电脑时所遇到的:文件系统。文件系统由目录和文件组成。每个目录都可以装内容。目录的内容可以是文件,也可以是目录。
按照这种方式,计算机的文件系统就是以递归结构来组织的。如果你想要描述这样的数据结构,那么你可以使用组合模式Composite。


适用场景:
你想表示对象的部分-整体层次结构
你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。


总结:
组合模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以像处理简单元素一样来处理复杂元素。
如果你想要创建层次结构,并可以在其中以相同的方式对待所有元素,那么组合模式就是最理想的选择。




6、享元模式
享元模式的特点是,复用我们内存中已存在的对象,降低系统创建对象实例的性能消耗。
享元模式的意图是通过共享有效支持大量细粒度的对象,来提高应用程序的性能,节省系统中重复创建对象实例的性能消耗
含义:
当我们系统中某个对象类型的实例较多的情况。
并且要求这些实例进行分类后,发现真正有区别的分类很少的情况。


使用场景:
当我们发现某个类型的对象有大量的实例时,我们是否可以对这些实例进行分类,经过分类后,我们发现只有很少的类别的情况下。
我们发现通过使用享元模式后能够提高系统的性能和不会带来更多的复杂度时。


连接池、输入法不同的文字


结构:
根据对象类型动态的创建对象实例。
根据对象池中的配置,在对象池中找到空闲的实体提供给程序调用,减少创建对象的次数。
我们需要设计每个类型的缓冲池,通过把对象进行缓存,提供性能。如果对象池中的对象长期不会调用,那么我们会提供一个销毁对象的机制。




五、访问者模式、中介者模式、解释器模式


1、访问者模式


1)涉及角色:
Visitor:定义一个访问操作(visit()),他以一个被访问者作为参数;抽象访问者角色,为该对象结构中具体元素角色声明一个访问操作接口。
该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它。
ConcreteVisitor:具体访问者角色,实现Visitor声明的角色
Element:定义一个接受访问操作(accept()),它以一个访问者(Visitor)作为参数。
ConcreteElement:具体元素,实现了抽象元素(Element)所定义的接受操作接口。
ObjectStructure 结构对象角色,这是使用访问者模式必备的角色。
它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)。


2)优点:
符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,
另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。
扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展。


3)适用情况:
一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor模式使得你可以将相关的操作集中起来 定义在一个类中。
当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能
还是在这些类中定义这些操作较好。




2、中介者模式(Mediator Pattern)
定义一个中介对象来封装系列对象之间的交互。中介者使各个对象不需要显示地相互引用,从而使其耦合性松散,而且可以独立地改变他们之间的交互。
通过中介者,这些对象不用相互知道


适用场景:
一组定义良好的对象,现在要进行复杂的通信。
定制一个分布在多个类中的行为,而又不想生成太多的子类。


优点:
降低了系统对象之间的耦合性,使得对象易于独立的被复用。
提高系统的灵活性,使得系统易于扩展和维护。


缺点:
中介者模式的缺点是显而易见的,因为这个“中介“承担了较多的责任,所以一旦这个中介对象出现了问题,那么整个系统就会受到重大的影响。




3、解释器模式
定义:给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。
用的比较少,例如正则表达式




六、观察者模式、迭代子模式、责任链模式、命令模式


1、观察者模式


又称发布-订阅模式、模型-视图模式、源-收听者模式或从属模式
一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
观察者设计模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。


观察者模式(Observer)完美的将观察者和被观察的对象分离开


结构与过程:
观察者(Observer):将自己注册到被观察对象(Subject)中,被观察对象将观察者存放在一个容器(Container)里(addObserver方法)。
被观察者(Subject):被观察对象发生了某种变化,从容器中得到所有注册过的观察者,将变化通知观察者(notifyObserver方法)。
撤销观察:观察者告诉被观察者要撤销观察,被观察者从容器中将观察者去除(deleteObsrver方法)。




2、迭代子模式


1)interface:
Iterator:first(),next(),hasNext()
Collection:add(),size(),iterator()


2)两个实现类:
MyIterator:
MyCollection:




3、责任链模式(Chain of Responsibility)
在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。
发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。


4、命令模式(Command)
在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。
但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。
在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern)。


来自客户端的请求传入一个对象,从而使你可用不同的请求对客户进行参数化。用于“行为请求者”与“行为实现者”解耦,可实现二者之间的松耦合,以便适应变化。分离变化与不变的因素。在面向对象的程序设计中,一个对象调用另一个对象,一般情况下的调用过程是:创建目标对象实例;设置调用参数;调用目标对象的方法。但在有些情况下有必要使用一个专门的类对这种调用过程加以封装,我们把这种专门的类称作command类。


适用场景:
a)整个调用过程比较繁杂,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。
b)调用前后需要对调用参数进行某些处理。
c)调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等。




优点:
a)将调用操作的对象和知道如何实现该操作的对象解耦。
b)Command是头等对象。他们可以像其他对象一样被操作和扩展。
c)你可将多个命令装配成一个符合命令。
d)增加新的Command很容易,因为这无需改变现有的类。


待续。。。。。




七、备忘录模式、状态模式


1、备忘录模式(Memento Pattern)
备忘录模式(Memento Pattern)又叫做快照模式(Snapshot Pattern)或Token模式,是GoF的23种设计模式之一,属于行为模式。


定义:在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。


涉及角色:
Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘
录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作。


优点:
有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这时,使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从
而可以恰当地保持封装的边界。
本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需要的这些状态的版本。
当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。


缺点:
如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
当负责人角色将一个备忘录 存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否很昂贵。
当发起人角色的状态改变的时候,有可能这个协议无效。如果状态改变的成功率不高的话,不如采取“假如”协议模式。


适用场景:
保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。
中国象棋悔棋(Caretaker持有MementoList)




2、状态模式(State Pattern)
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。


结构:
上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。
抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
具体状态(Concrete State):实现抽象状态定义的接口。


优点:
状态模式将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。
所有状态相关的代码都存在于某个ConcereteState中,所以通过定义新的子类很容易地增加新的状态和转换。
状态模式通过把各种状态转移逻辑分不到State的子类之间,来减少相互间的依赖。


缺点:
导致较多的ConcreteState子类


适用场景:
一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。


策略模式是让用户指定更换的策略算法,而状态模式是状态在满足一定条件下的自动更换,用户无法指定状态,最多只能设置初始状态。 





猜你喜欢

转载自blog.csdn.net/panzihao_beijing/article/details/79710112