责任型模式包括了:责任链模式、单例模式、观察者模式、中介者模式、代理模式和享元模式。
1、责任链模式(Chain of Responsibility)
为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
优点:1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点:1、不能保证请求一定被接收。 2、在找到正确的处理对象之前,所有的条件判定都要执行一遍,当责任链过长时,可能会引起性能的问题。
使用场景:一个系统的审批需要多个对象才能完成处理的情况下,例如请假系统等。或者代码中存在多个if-else语句的情况下,此时可以考虑使用责任链模式来对代码进行重构。
举例:请假经常会遇到,1天内直接跟你上司请,比如说经理;2天到5天内的话就要找副总裁处理;6天到10天的话就要总裁审批了,这个时候,如果你要递交请假申请,就要依据你的请假天数进行判断到底由谁来审批了,如果你要定义一个方法来处理请假审批的话,如果审批人的更改,或者请假天数与审批人的对应关系有变动,就需要进行代码的修改。而且如果审批流程过细,审批人过多,代码中就会存在很多if-else语句,这时候就使用责任链来实现。
第一步:创建请假的基本类型
第二步:创建抽象的审批人类
第三步:创建实现扩展了该审批人抽象类的实体类
第四步:创建不同类型的审批人。赋予它们不同的审批级别,并在每个审批人中设置下一个审批人。每个审批人中的下一个审批人代表的是链的一部分。
最后:发起请假流程
输出:
2、单例模式(Singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
优点:1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。 2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:1、要求生产唯一序列号。 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
单例的实现:
1、无线程安全的单例模式:
2、线程安全的单例模式:
3、观察者模式(Observer)
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
优点:1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点:1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
举例:给每个订阅者发送通知
第一步:创建观察者接口和平台抽象类,实现抽象耦合
第二步:创建平台的招聘中心实体类,继承抽象平台类
第三步:创建一个订阅者类,继承观察者接口(意思就是通过观察者观察到平台的招聘中心有状态发生改变,就通知订阅者)
最后:进行订阅操作
输出:
4、 中介者模式(Mediator)
如果对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,也需要跟踪与之相关联的对象,同时做出相应的处理。所以,用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。但不应当在职责混乱的时候使用。
优点:1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
缺点:中介者会庞大,变得复杂难以维护。
使用场景:1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。比如说用在机场调度系,广东麻将的豆豆的计算
举例:广东麻将,简单点就做一个只能自摸才会算豆豆的这种玩法(好吧,好像一点都不好玩,只能自摸赢3家)
第一步:创建打麻将的牌友抽象类(待会会有4个牌友实现这个抽象类)
第二步:创建抽象中介类
第三步:创建实现抽象中介类的实体类
第四步:创建4个牌友继承牌友抽象类
最后:初始化麻将的4个牌友,请个中介来算豆豆
输出结果:
5、代理模式(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
优点:1、职责清晰。 2、高扩展性。 3、智能化。
缺点:1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
举例:同事A找同事B帮忙韩国的化妆品,同事B就找了韩国的旅游的朋友帮忙代购化妆品。同事B就是一个代理者。
第一步:创建人的抽象类
第二步:创建一个代理买东西的人(因为他认识真正买东西的人),却不在国外的家伙
第三步:创建一个真正在国外的家伙(真正执行者)
最后:代理买东西去
输出:
代理模式主要是,外部执行代理对象的时候根本不知道真正的执行者是谁。
6、享元模式(Flyweight)
主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。运用共享技术有效地支持大量细粒度的对象。在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
优点:大大减少对象的创建,降低系统的内存,使效率提高。
缺点:
1、为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑更复杂,使系统复杂化。
2、享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
使用场景:
1、一个系统中有大量的对象;
2、这些对象耗费大量的内存;
3、这些对象中的状态大部分都可以被外部化
4、这些对象可以按照内部状态分成很多的组,当把外部对象从对象中剔除时,每一个组都可以仅用一个对象代替
5、软件系统不依赖这些对象的身份
满足上面的条件的系统可以使用享元模式。但是使用享元模式需要额外维护一个记录子系统已有的所有享元的表,而这也需要耗费资源,所以,应当在有足够多的享元实例可共享时才值得使用享元模式。
注意事项: 1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。
举例:一个文本编辑器中会出现很多字面,使用享元模式去实现这个文本编辑器的话,会把每个字面做成一个享元对象。享元对象的内部状态就是这个字面,而字母在文本中的位置和字体风格等其他信息就是它的外部状态。
第一步:创建抽象享元类
第二步:创建享元实体类
第三步:创建享元工厂类
最后:使用享元对象的外部状态
输出:
享元模式主要用来解决由于大量的细粒度对象所造成的内存开销的问题,它在实际的开发中并不常用,可以作为底层的提升性能的一种手段。