导语
我们习惯去说类和方法承担着各种各样的职责。事实上,这通常意味着你有责任提供健壮的设计,并让代码承担合适的功能。幸运的是,Java语言分担了一部分责任。我们可以限制类、字段和方法的可见性,从而去限制其他开发人员对你开发的代码的调用。可见性向读者展示了该如何暴露类的部分内容。
单例模式-Singleton
- 复杂度:低
- 适用度:广
- 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
有两种场景较为适合单例模式的使用:
- 当你需要一个全局唯一的对象去处理业务的时候,此时可以通过单例阻塞其他调用
- 当系统实例化了大量不必要的对象时
懒汉与饿汉模式的区别主要在于使用场景上,如果当前单例对象的初始化不依赖与其他资源的加载,则可以使用饿汉方式提前加载实例,但如果单例对象的初始化存在前置条件,则需要使用懒汉模式,并在预处理完毕时,初始化这个单例对象
观察者模式-Observer
- 复杂度:中
- 适用度:较广
- 意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
理解Observer的核心在于监听(观察)和通知,创建一个主题(Subject),然后将主题添加到**监听(观察者)**中,同时主题也会持有所有添加了它的监听的引用(通常会维护一个List保存所有的观察者).此时Subject若发生变化(或执行动作),则可以通过它持有的观察者的引用,通知到观察者们.
P.S.Observer有两个要点需要注意:1.直接观察者,间接观察者过多会导致程序复杂度上升,甚至循环依赖的情况;2.过多的观察者在执行业务的时候,会导致运行效率问题(例如每个观察者都要发起一个HTTP请求,此时由于程序是顺序执行,会导致系统延迟),需要考虑并发处理通知
调停者模式-Mediator
- 复杂度:低
- 适用度:广
- 意图:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
理解mediator的关键在于要解决什么样的问题,例如转账行为,当A要给B转账的时候,面向对象会要求A持有B的引用,除了A->B,还可能有B->A,A<->C,B<->D...这个时候,你需要有一个交易平台处理这些转账的业务.这个平台对象,就是调停者.需要注意的是A,B,C...不一定是同一个抽象概念,笼统地将如果你的业务存在大量集合之间的交互<->,那么你就需要一个mediator维护这些对象之间的关系.
P.S.mediator模式会导致大量的对象和业务放在一个类(mediator类)中处理,此时需要格外注意这些实体职能的区分,暴露需要交互的方法,同时将其他业务下沉,尽可能使调停者负责的功能更纯粹.
Mediator
代理模式-Proxy
- 复杂度:低
- 适用度:较广
- 意图:为其他对象提供一种代理以控制对这个对象的访问。
Proxy是一个使用场景不太明显的模式,通常当你需要隐藏或通过添加权限的方式限制某些类的使用权限市,可以使用代理模式.
代理模式的核心在于限制真实对象的使用权限
P.S.对于适配器模式,门面模式,代理模式可以放在一起理解,而这种理解的基础,并不是建立在面向对象的UML结构上,而是要建立在它们的适用场景上.
适配器模式:通过接口封装,适配Client的需求
门面模式:将众多子系统的功能,封装(组合)到一个门面对象中,供Client使用
代理模式:将真实的使用类隐藏,通过代理对象,附加其他功能(如有没有权限,需要调用具体那个类的实例等)
责任链模式-Chain of Responsibility
- 复杂度:中
- 适用度:一般
- 意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
责任链模式的适用环境有一个明显的特征:即需要对同一个对象(或同一组数据),执行多次的操作,例如消息的处理,可以拆分成:屏蔽敏感词,处理特殊符号,处理表情,屏蔽安全指令...又如JavaWeb中的Filter过滤器:解析Http协议,处理编码格式...
责任链模式的特征在于,超类(接口)定义了必要的抽象行为和必要的限制(如不同子类的执行顺序,条件等),子类负责实现这些抽象行为.需要注意的是:
- 责任链主要简化了调用端的复杂度,你可以将任意需要执行的逻辑,加入到责任链的执行环节,而无需关注他的内部
- 责任链的子类,如果有必要顺序要求,可以通过持有兄弟类引用的方式,保证自身的执行顺序
- 责任链并不保证每个环节都一定会执行,同时由于复杂度的提高,还可能出现同一个子类反复调用的情况
P.S.提到责任链会让我联想起workflow(工作流),但workflow和chain of responsibility并非一个层面的问题,工作流是更高层面的实现,并且未必会用到责任链模式,两者仅在抽象结构上有一些类似而已.
享元模式-Flyweight
- 复杂度:低
- 适用度:广
- 意图:运用共享技术有效地支持大量细粒度的对象。
Flyweight用于处理系统频繁大量创建对象的场景,最典型的应用就是String对象初始化的逻辑,对于String类型的初始化:
- 当初始化一个String字符串时,会先查看内存中是否存在这个字符串
- 内存中不存在时,将会分配一段空间存储当前的字符串
- 如果存在,则后续的引用将会直接使用这段内存的数据,而非创建一段有着相同内容的对象
当然,String还有一些其他的优化,类似的还有数据库连接池的逻辑(连接池中会常驻一部分长连接,当有CURD请求到达时,将会直接使用连接池中已经建立好连接的对象,而非创建新的连接),这样做的好处是能够有效的减少每次创建tcp连接所浪费的时间.
对于Java端的实现,关键在于:
- 享元空间,通常是一个Map结构
- 工厂对象,通过工厂对象对要实例化的对象进行管理
P.S.需要格外注意的是享元模式要区分出哪些属性是可以共享,哪些属性是不可以共享的,因为一旦出现混乱,将导致共用属性被非法修改,或是创建了大量非必要的公共属性.
小结
文章不再对具体的案例做分析,重点在于指出这些设计模式的核心特征和适用范围.从分类(职责型模式)的角度上来看:
- Singleton侧重点在于你的业务需要一些全局唯一的角色处理问题,单一的对象保证了能够获取到一些同步资源(如Spring中的Bean,默认就是使用单例),同时避免了大量新的功能类似的对象被创建.
- Observe的重心在于你有一些联动触发的场景,或者说你的某些类的行为会影响到其他众多类(之所以强调众多的原因是,如果只有单一的成员变量受到影响,你并不一定非要使用Observe,你需要一些基础的设计理念,例如开闭原则,扩展能力)
- Mediator有趣的地方在于,你可以把那些试图用一个类完成所有工作的行为都定义成调停者模式,但这显然不是Mediator的初衷,我们需要花费更多的精力去简化成员类的行为,已达到Mediator尽可能只维护这些成员之间的作用关系.
- Proxy在抽象理解上,建议和Facade,Adapter一起理解.
- Chain of responsibility是很典型的职责类模式,对于Client给到的请求(或数据),动态的将需要处理的环节,以责任链模式的方式加入到你的业务流程中.
- Flyweight可以理解成一个池,当你有业务需要对象运转时,会先检查是否有这样的对象,如果有就直接拿来运行,如果没有,就"喊"一个新的过来,运行完就放在池中等待下次的使用.或许你可以在添加一些销毁长时间不用的对象的逻辑代码,这样看是不是很像GC?
本文由 momoker 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Jun 21,2023