设计模式之禅
秦小波
Citation (APA): 秦小波. (2010). 设计模式之禅 [Kindle Android version]. Retrieved from Amazon.com
单例模式和原型模式非常容易理解,单例模式是要保持在内存中只有一个对象,原型模式是要求通过复制的方式产生一个新的对象,这两个不容易混淆。
工厂方法模式注重的是整体对象的创建方法,而建造者模式注重的是部件构建的过程,旨在通过一步一步的精确构造创建出一个复杂的对象。
一般来说,工厂方法模式的对象粒度比较粗,建造者模式的产品对象粒度比较细。
如果需要详细关注一个产品部件的生产、安装步骤,则选择建造者,否则选择工厂方法模式。
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
为什么叫结构类模式呢?因为它们都是通过组合类或对象产生更大结构以适应更高层次的逻辑需求。
装饰模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点则是代理模式着重对代理过程的控制,而装饰模式则是对类的功能进行加强或减弱,它着重类的功能变化,
代理模式和装饰模式有非常相似的地方,甚至代码实现都非常相似,特别是装饰模式中省略抽象装饰角色后,两者代码基本上相同,但是还是有细微的差别。代理模式是把当前的行为或功能委托给其他对象执行,代理类负责接口限定:是否可以调用真实角色,以及是否对发送到真实角色的消息进行变形处理,它不对被主题角色(也就是被代理类)的功能做任何处理,保证原汁原味的调用。代理模式使用到极致开发就是AOP,这是各位采用Spring架构开发必然要使用到的技术,它就是使用了代理和反射的技术。装饰模式是在要保证接口不变的情况下加强类的功能,它保证的是被修饰的对象功能比原始对象丰富(当然,也可以减弱),但不做准入条件判断和准入参数过滤,如是否可以执行类的功能,过滤输入参数是否合规等,这不是装饰模式关心的。
装饰模式和适配器模式在通用类图上没有太多的相似点,差别比较大,但是它们的功能有相似的地方:都是包装作用,都是通过委托方式实现其功能。不同点是:装饰模式包装的是自己的兄弟类,隶属于同一个家族(相同接口或父类),适配器模式则修饰非血缘关系类,把一个非本家族的对象伪装成本家族的对象,注意是伪装,因此它的本质还是非相同接口的对象。
装饰模式很容易扩展!今天不用这个修饰,好,去掉;明天想再使用,好,加上。这都没有问题。而且装饰类可以继续扩展下去;但是适配器模式就不同了,它在两个不同对象之间架起了一座沟通的桥梁,建立容易,去掉就比较困难了,需要从系统整体考虑是否能够撤销。
行为类模式包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。
命令模式和策略模式的类图确实很相似,只是命令模式多了一个接收者(Receiver)角色。它们虽然同为行为类模式,但是两者的区别还是很明显的。策略模式的意图是封装算法,它认为“算法”已经是一个完整的、不可拆分的原子业务(注意这里是原子业务,而不是原子对象),即其意图是让这些算法独立,并且可以相互替换,让行为的变化独立于拥有行为的客户;而命令模式则是对动作的解耦,把一个动作的执行分为执行对象(接收者角色)、执行行为(命令角色),让两者相互独立而不相互影响。
策略模式和命令模式相似,特别是命令模式退化时,比如无接收者(接收者非常简单或者接收者是一个Java的基础操作,无需专门编写一个接收者),在这种情况下,命令模式和策略模式的类图完全一样,代码实现也比较类似,但是两者还是有区别的。
策略模式关注的是算法替换的问题,一个新的算法投产,旧算法退休,或者提供多种算法由调用者自己选择使用,算法的自由更替是它实现的要点。换句话说,策略模式关注的是算法的完整性、封装性,只有具备了这两个条件才能保证其可以自由切换。命令模式则关注的是解耦问题,如何让请求者和执行者解耦是它需要首先解决的,解耦的要求就是把请求的内容封装为一个一个的命令,由接收者执行。由于封装成了命令,就同时可以对命令进行多种处理,例如撤销、记录等。
策略模式中的抽象算法和具体算法与命令模式的接收者非常相似,但是它们的职责不同。策略模式中的具体算法是负责一个完整算法逻辑,它是不可再拆分的原子业务单元,一旦变更就是对算法整体的变更。
策略模式适用于算法要求变换的场景,而命令模式适用于解耦两个有紧耦合关系的对象场合或者多命令多撤销的场景。
策略模式封装的是不同的算法,算法之间没有交互,以达到算法可以自由切换的目的;而状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的。这两种模式虽然都有变换的行为,但是两者的目标却是不同的。
策略模式的环境角色只是一个委托作用,负责算法的替换;而状态模式的环境角色不仅仅是委托行为,它还具有登记状态变化的功能,与具体的状态类协作,共同完成状态切换行为随之切换的任务。
策略模式只是确保算法可以自由切换,但是什么时候用什么算法它决定不了;而状态模式对外暴露的是行为,状态的变化一般是由环境角色和具体状态共同完成的,也就是说状态模式封装了状态的变化而暴露了不同的行为或行为结果。
在责任链模式中,上下节点没有关系,都是接收同样的对象,所有传递的对象都是从链首传递过来,上一节点是什么没有关系,只要按照自己的逻辑处理就成。而触发链模式就不同了,它的上下级关系很亲密,下级对上级顶礼膜拜,上级对下级绝对信任,链中的任意两个相邻节点都是一个牢固的独立团体。
在责任链模式中,一个消息从链首传递进来后,就开始沿着链条向链尾运动,方向是单一的、固定的;而触发链模式则不同,由于它采用的是观察者模式,所以有非常大的灵活性,一个消息传递到链首后,具体怎么传递是不固定的,可以以广播方式传递,也可以以跳跃方式传递,这取决于处理消息的逻辑。
创建类模式描述如何创建对象,行为类模式关注如何管理对象的行为,结构类模式则着重于如何建立一个软件结构,虽然三种模式的着重点不同,
策略模式实现两种算法的自由切换,它提供了这样的保证:封装邮件的两种行为是可选择的,至于选择哪个算法是由上层模块决定的。策略模式要完成的任务就是提供两种可以替换的算法。
桥梁模式关注的是抽象和实现的分离,它是结构型模式,
简单来说,策略模式是使用继承和多态建立一套可以自由切换算法的模式,桥梁模式是在不破坏封装的前提下解决抽象和实现都可以独立扩展的模式。桥梁模式必然有两个“桥墩”——抽象化角色和实现化角色,只要桥墩搭建好,桥就有了,而策略模式只有一个抽象角色,可以没有实现,也可以有很多实现。
门面模式为复杂的子系统提供一个统一的访问界面,它定义的是一个高层接口,该接口使得子系统更加容易使用,避免外部模块深入到子系统内部而产生与子系统内部细节耦合的问题。中介者模式使用一个中介对象来封装一系列同事对象的交互行为,它使各对象之间不再显式地引用,从而使其耦合松散,建立一个可扩展的应用架构。
门面模式和中介者模式之间的区别还是比较明显的,门面模式是以封装和隔离为主要任务,而中介者模式则是以调和同事类之间的关系为主,因为要调和,所以具有了部分的业务逻辑控制。
门面模式只是增加了一个门面,它对子系统来说没有增加任何的功能,子系统若脱离门面模式是完全可以独立运行的。而中介者模式则增加了业务功能,它把各个同事类中的原有耦合关系移植到了中介者,同事类不可能脱离中介者而独立存在,除非是想增加系统的复杂性和降低扩展性。
门面模式是一种简单的封装,所有的请求处理都委托给子系统完成,而中介者模式则需要有一个中心,由中心协调同事类完成,并且中心本身也完成部分业务,它属于更进一步的业务功能封装。
包装模式包括:装饰模式、适配器模式、门面模式、代理模式、桥梁模式。
5个包装模式是大家在系统设计中经常会用到的模式,它们具有相似的特征:都是通过委托的方式对一个对象或一系列对象(例如门面模式)施行包装,有了包装,设计的系统才更加灵活、稳定,并且极具扩展性。从实现的角度来看,它们都是代理的一种具体表现形式,
装饰模式是一种特殊的代理模式,它倡导的是在不改变接口的前提下为对象增强功能,或者动态添加额外职责。就扩展性而言,它比子类更加灵活,例如在一个已经运行的项目中,可以很轻松地通过增加装饰类来扩展系统的功能。适配器模式的主要意图是接口转换,把一个对象的接口转换成系统希望的另外一个接口,这里的系统指的不仅仅是一个应用,也可能是某个环境,比如通过接口转换可以屏蔽外界接口,以免外界接口深入系统内部,从而提高系统的稳定性和可靠性。桥梁模式是在抽象层产生耦合,解决的是自行扩展的问题,它可以使两个有耦合关系的对象互不影响地扩展,比如对于使用笔画图这样的需求,可以采用桥梁模式设计成用什么笔(铅笔、毛笔)画什么图(圆形、方形)的方案,至于以后需求的变更,如增加笔的类型,增加图形等,对该设计来说是小菜一碟。门面模式是一个粗粒度的封装,它提供一个方便访问子系统的接口,不具有任何的业务逻辑,仅仅是一个访问复杂系统的快速通道,没有它,子系统照样运行,有了它,只是更方便访问而已。