今天是设计模式连载的第二篇,记录了代理模式,原型模式,中介者模式,命令模式。同样每个设计模式的详解都包括的我的理解以及我的分析推导过程。
代理模式
理解:该设计模式主要是对实际对象调用的扩展,实际对象仅需关注主体业务即可,其他的扩展比如访问限制,预处理之类的可在代理类中完成
分析/推导:
- 扩展实际对象的功能,实际对象关注业务,其他的扩展则放在代理类中完成,算是比较典型的对修改封闭,对扩展开放。
动态代理:
- 动态代理是典型代理模式的一种变化,典型代理模式必须在定义时即确定好是对哪个接口进行代理,并且只能代理一组确定的接口。
- 动态代理则理论上可以对任何接口进行代理,而具体方法的调用则是通过调用具体确定的
java.lang.reflect.InvocationHandler#invoke
方法来完成的。
原型模式
理解:该设计模式比较简单,指的是对原对象进行克隆,保持原对象类型,对其属性进行复制。
分析/推导:
- 在java中,你仅需要实现该接口
java.lang.Cloneable
,标记一个类为可克隆,然后覆盖java.lang.Object#clone
方法即可。 - 使用克隆实际上是对内存二进制流的拷贝,并且并不会执行类的构造函数。
- 优点:直接拷贝二进制流,在需要构建大量对象时相比
new
来说又很大的性能提升 - 缺点:不会调用
new
构造函数,肯能会绕过一些初始化的流程。
中介者模式
理解:该设计模式是为了解决多个业务对象两两之间依赖调用特别复杂而产生的。在多个业务对象中增加一个中介者,用来管理任意两个对象间的依赖调用,即任意对象的调用改为通过中介者来转发。
分析/推导:
- 首先看下类两两间相互依赖的类图(由于依赖关系比较负责,仅画出了A模块的依赖关系)。
- 为了解决这种复杂的相互依赖关系,使类与类之间的关系更加清晰简洁,增加了中介者模块,实际负责类与类之间的交互,即 A调用B改为A调用中介者,再由中介者最终调用B。
- 优点,简化了类与类之间的依赖关系,使得任意业务类仅与中介者耦合,业务类的改动仅需对中介者负责即可。
- 所有业务类的改动,仅需保证中介者测试通过即可,如果未加入中介者模块,则一个改动需要所有依赖方都需要进行测试保证没有问题。
- 缺点,中介者可能会非常臃肿,实际应用中类与类间多依赖是很正常的,不建议盲目使用中介者模式,只有当类图间的依赖关系非常繁杂时,可以增加中介者模块来使得类与类的关系变得更加清晰。
命令模式
理解:简化上层模块对下层业务的感知。当完成一个业务需要调用3-4个下层模块并且需感知调用顺序的时候,可以抽出一个命令者模块,由具体的命令者来完成对下层模块的调用。
分析/推导:
- 客户端的业务是多种多样的,每个业务都可能需要多个底层模块来完成他。
- 上层模块不期望了解底层的相关逻辑。
- 可以将客户端的所有业务都封装成一个个command(指令),而每个指令则由具体的一个receiver(接收者)来接收然后具体分发执行下去由相关的底层模块来执行(receiver(接收者)可以简单预处理,但具体的指令执行仍然是由各个底层模块来完成)。
- 举例:我们平常经常写的controller –> manager –> service可以简单理解为该设计模式。
- controller不需要知道底层service的相关逻辑,而是将客户端的某个业务指令进行封装,然后调用manager
- 一个manager则是一个指令的receiver(接收者),而manager中的一个方法则对应一个command(指令)。
- manager来管理具体的一个指令相关需要调用的service并执行。
- service则是具体的底层模块。
- 举例2:想想
org.springframework.web.servlet.DispatcherServlet
,他不就是接收客户端封装好的request(command),然后负责管理具体调用哪个controller吗。 - 优点:上层无需感知底层逻辑。上层和底层间增加了command的receiver(接收者),隔离了上层对底层的直接依赖,降低了耦合,底层的改动直接对receiver(接收者)负责即可,上层无需感知。
- 想想看,如果没有manager,service的一个改动,所有依赖这个service的controller都可能会受影响,需要把相关的单元测试都跑一遍,这是多么痛苦的。