前言
行为模式是描述多个类与对象之间通过协作共同完成单个对象无法单独完成的任务。
行为模式分为:
- 类行为模式通过集成在类之间分派行为
- 对象行为模式通过组合或聚合在对象之间分配行为
行为模式:
- 模板方法模式:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类
- 命令模式:将请求封装为对象,将发出请求的责任与执行请求的责任分开
- 访问者模式:不改变集合元素前提下,为每一个元素提供多种访问方式
- 迭代器模式:提供一种顺序访问据需对象的方法,不暴露聚合对象的内部表示
- 观察者模式:多个对象存在一对多关系,当一个对象发生变化,通知到其他多个对象,从而影响其他对象行为
- 中介模式:定义一个独享简化原有对象之间的关系,减低对象间的耦合性,使原有对象之间不许相互了解
- 备忘录模式:不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复
- 解释器模式:提供如何定义语言的文法,以及对语言的解释方法
- 状态模式:允许一个对象再起内部状态发生拜年话事改变其行为能力
- 策略模式:定义一系列算法,并进行封装,使其可以相互替换,切算法替换不会影响客户端
- 职责链模式:把请求从一个对象传递到下一个对象,知道请求被响应为止。去除对象之间的耦合。
正文
13. 模板方法模式(Template Method)
定义一个操作中的算法骨架,将一些步骤延迟到子类中,使子类可以不改变一个算法的结构,子类可以重定义该算法的一些特定步骤
优点:
- 封装了不变的部分,扩展可变部分,把认为不变的部分算法封装到父类中,可变部分有子类集成涉嫌,便于子类扩展
- 提取公共部分代码,实现代码复用
- 部分方法由子类实现,子类可以通过扩展方式增加功能,符合开闭原则
缺点:
- 每个不同实现都需要实现,类个数增加
- 父类中的抽象方法由子类实现,子类的执行结构影响父类结构,会提高代码的可读性
- 由于继承的问题,一旦修改父类抽象方法,所有子类都需要修改
适用场景:
- 算法整体步骤很固定,但是个别容易变化
- 多个子类存在公共行为,可以进行提取到公共类,避免重复
模板方法UML:
代码如下:
抽象类:
public abstract class AbstractClass { public final void templateMethod() { specificMethod(); abstractMethod1(); abstractMethod2(); } private void specificMethod() { System.out.println("I am specific method"); } protected abstract void abstractMethod1(); protected abstract void abstractMethod2(); }
具体子类
public class ConcreteClass extends AbstractClass { @Override protected void abstractMethod1() { System.out.println("I am concrete method1"); } @Override protected void abstractMethod2() { System.out.println("I am concrete method1"); } }
客户端
public class Client { public static void main(String[] args) { AbstractClass abstractClass = new ConcreteClass(); abstractClass.templateMethod(); /** * I am specific method * I am concrete method1 * I am concrete method1 */ } }
小结:
算法只存在于一个地方,也就是父类中,容易修改。最大化代码复用,父类中的末班方法和已实现的某些步骤,子类可以直接使用,统一了算法,并且提供了河大灵活性,子类可以使用钩子方法修改部分算法步骤。当在完成某个过程时,这个过程需要执行一些列的步骤,但步骤基本相同,可能个别实现不同,可以使用模板方法。例如我在开发用户操作行为日志,抽离出各个系统记录日志的统一行为,并对外部提供钩子方法。实现很大程度的代码复用。当然是用模板方法时,要控制子类的扩展,也就是可以要求子类只实现钩子方法。其余的模板方法可以加final或private修饰。
14. 命令模式(Command)
将一个请求封装为一个对象,使请求责任与执行责任分隔开。将请求发送者与请求接受者消除彼此之间的耦合,是对象之间调用关系更加灵活,实现解耦。
优点:
- 引入中间件,降低系统耦合
- 扩展性好,增加或者删除命令方便,采用命令模式增加与删除命令不会影响其他子类,满足开闭原则
缺点:
- 可能会产生大量的命令类,每一个操作都需要设计一个具体的命令类,增加系统的复杂性
- 引入额外类型结构(请求方与抽象命令接口),增加理解上的困难
适用场景:
- 请求调用者需要与请求接受者解耦
- 请求命令经常进行修改
命令模式UML:
角色:
- 抽象命令角色:声明执行命令的接口,拥有执行命令的抽象方法
- 具体命令角色:实现类,拥有接受者对象,通过调用接收者功能来完成命令要执行的操作
- 接收者角色:执行命令的相关操作,具体命令对象业务的真正实现者
- 调用者:请求的发送者,通常拥有很多命令对象,放过访问命令对象执行相关请求,不直接访问接收者
代码如下:
抽象命令:
public interface Command { void execute(); }
具体命令:
public class ConcreteCommandA implements Command { private final ReceiverA receiverA; public ConcreteCommandA(ReceiverA receiverA) { this.receiverA = receiverA; } @Override public void execute() { receiverA.action("concrete command A"); } } public class ConcreteCommandB implements Command { private final ReceiverB receiverB; public ConcreteCommandB(ReceiverB receiverB) { this.receiverB = receiverB; } @Override public void execute() { receiverB.action("concrete command B"); } }
接收者
public class ReceiverA { public void action(String command) { System.out.println("I am ReceiverA action, execute: " + command); } } public class ReceiverB { public void action(String command) { System.out.println("I am receiverB action, execute: " + command); } }
调用者:
public class Invoker { //r如果Command是一个数组,初始化时可以全部初始华为EmptyCommand,这样可以再每次调用命令时不必进行判空操作 private Command command = new EmptyCommand(); public Invoker(Command command) { this.command = command; } public void setCommand(Command command) { this.command = command; } public void call() { command.execute(); } }
客户端:
public class Client { public static void main(String[] args) { Invoker invoker = new Invoker(new ConcreteCommandA(new ReceiverA())); invoker.call(); invoker.setCommand(new ConcreteCommandB(new ReceiverB())); invoker.call(); /** * I am ReceiverA action, execute: concrete command A * I am receiverB action, execute: concrete command B */ } }
空命令:
public class EmptyCommand implements Command { @Override public void execute() { // do nothing } }
小结:
请求调用者与请求接收者之间解耦是通过命令对象山西爱你,命令对象起到纽带桥梁作用。
可以设计一个命令队列,实现多线程执行命令
在进行设计时,可以增加空命令,省去判空的操作
15. 访问者模式(Visitor)
封装一些作用于某种数据结构的各元素的操作,在不改变数据结构的前提下定义作用于这些元素的新操作。
优点:
- 扩展性好,可以再不修改对象结构中的元素的前提下,对对象结构中元素添加新的功能
- 复用性好,通过访问者定义整个对象结构通用性的功能,提高系统的复用程度
- 灵活性好,访问者模式可以将数据结构与作用于结构上的操作记性解耦,使得操作集合可以相对自由的变化二步影响数据结构
- 符合单一职责原则,访问者模式将相关行为封装在一起,构成一个访问者,使每一个访问者功能都比较单一
缺点:
- 增加新的元素类比较困难,每增加一个新的元素类,都需要在每一个具体访问者类中增加响应操作,违背开闭原则
- 破坏封装。具体元素对访问者公布细节,破坏对象的封装性
- 违背依赖倒置原则,访问者模式依赖具体类,而非抽象类
适用场景:
- 对一个对象结构中的对象进行很多不同的操作(这些操作彼此没有关联),同时需要避免让这些操作污染这些对象的类
访问者模式UML
角色:
- 抽象访问者:定义一个访问具体元素的接口,为每一个具体元素类对应一个访问操作的visit,该操作中参数类型标识被访问具体元素
- 具体访问者:实现访问操作,确定访问者访问一个元素时该做什么
- 抽象元素:声明一个包含接受操作的accept接口,被接受的访问者对象作为accept的参数
- 具体元素:实现抽象元素,其方法体一般都是调用访问者的visit接口,另外具体元素中一般会包含业务逻辑的相关操作
- 对象结构:一个包含元素角色的容器,提供让访问者遍历容器中所有元素的方法
代码如下:
抽象元素:
public interface Element { void accept(Visitor visitor); }
具体元素:
public class ConcreteElementA implements Element { @Override public void accept(Visitor visitor) { visitor.visit(this); } public void operationA() { System.out.println("concrete element operation A used"); } } public class ConcreteElementB implements Element { @Override public void accept(Visitor visitor) { visitor.visit(this); } public void operationB() { System.out.println("concrete element B operation B was used"); } }
抽象访问者:
public interface Visitor { void visit(ConcreteElementA concreteElementA); void visit(ConcreteElementB concreteElementB); }
具体访问者:
public class ConcreteVisitorA implements Visitor { @Override public void visit(ConcreteElementA concreteElementA) { System.out.println("concrete visitorA will operation concrete element A"); concreteElementA.operationA(); } @Override public void visit(ConcreteElementB concreteElementB) { System.out.println("concrete visitorA will operation concrete element B"); concreteElementB.operationB(); } } public class ConcreteVisitorB implements Visitor { @Override public void visit(ConcreteElementA concreteElementA) { System.out.println("concrete visitorB will operation concrete element A"); concreteElementA.operationA(); } @Override public void visit(ConcreteElementB concreteElementB) { System.out.println("concrete visitorB will operation concrete element B"); concreteElementB.operationB(); } }
对象结构:
public class ObjectStructure { private final List<Element> elementList = new ArrayList<>(); public void accept(Visitor visitor) { elementList.forEach(element -> element.accept(visitor)); } public void add(Element element) { elementList.add(element); } public void remove(Element element) { elementList.remove(element); } }
客户端:
public class Client { public static void main(String[] args) { ConcreteElementA concreteElementA = new ConcreteElementA(); ConcreteElementB concreteElementB = new ConcreteElementB(); ObjectStructure objectStructure = new ObjectStructure(); objectStructure.add(concreteElementA); objectStructure.add(concreteElementB); objectStructure.accept(new ConcreteVisitorA()); objectStructure.accept(new ConcreteVisitorB()); /** * concrete visitorA will operation concrete element A * concrete element operation A used * concrete visitorA will operation concrete element B * concrete element B operation B was used * concrete visitorB will operation concrete element A * concrete element operation A used * concrete visitorB will operation concrete element B * concrete element B operation B was used */ } }
小结:
将数据的操作与结构进行分离,解决数据结构和操作耦合性,适用于数据结构稳定的系统
16. 迭代器模式(Iterator)
提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层标识,不暴露其内部的结构
优点:
- 访问一个局和对象无需暴露内部表示
- 遍历任务交给迭代器完成,简化聚合类
- 支持不同方式遍历集合
- 增加新的聚合类或者迭代器比较方便,无需修改原有代码
- 封装性良好,为遍历不同聚合结构提供统一接口
缺点:
- 增加类的个数
适用场景:
- 一般场景下不需要使用,开源框架都有提供。除非实现的数据结构的对应迭代器
迭代器的UML:
代码如下:
抽象聚合:
public interface Aggregate<T> { void add(T object); void remove(T object); Iterator<T> getIterator(); }
具体聚合:
public class ConcreteAggregate<T> implements Aggregate<T> { private final List<T> list = new ArrayList<>(); @Override public void add(T object) { list.add(object); } @Override public void remove(T object) { list.remove(object); } @Override public Iterator<T> getIterator() { return new ConcreteIterator<>(list); } }
抽象迭代器:
public interface Iterator<T> { T first(); T next(); boolean hasNext(); }
具体迭代器:
public class ConcreteIterator<T> implements Iterator<T> { private final List<T> list; private int index = 0; public ConcreteIterator(List<T> list) { this.list = list; } @Override public T first() { return list.get(0); } @Override public T next() { return list.get(index++); } @Override public boolean hasNext() { return list.size() > index; } }
客户端:
public class Client { public static void main(String[] args) { Aggregate<Integer> aggregate = new ConcreteAggregate<>(); aggregate.add(1); aggregate.add(2); aggregate.add(3); Iterator<Integer> iterator = aggregate.getIterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } System.out.println("first: " + iterator.first()); /** * 1 * 2 * 3 * first: 1 */ } }
小结:
这里有一个设计思想就是一个类应当只有一个因七百年画的原型。在聚合类中,把迭代器分开,就是要把管理对象集合与便利对象集合的责任分开。
17. 观察者模式(Observer)
这个模式应当是非常熟悉的,对象之间存在多对一依赖的一种设计,被以来的对象叫做Subject,依赖的对象叫做Observer。又称发布-订阅模式
优点:
- 降低了目标和观察者之间的耦合关系,两者之间时抽象耦合关系,符合依赖倒置原则
- 目标与观察者之间建立一套触发机制
缺点:
- 目标与观察者之间依赖关系并没有解除,有可能会出现循环引用
- 当观察者对象过多时,通知发布会花费较多时间
适用场景:
- 对象之间存在一对多关系,一个对象的状态发生变化会影响其他对象
- 类似于广播机制,不需关系收听者,只需要分发广播,系统中惯性取得对象自动接受该广播
- 多层级嵌套使用,形成链式触发机制
观察者模式UML
代码如下:
抽象目标:
public abstract class Subject { protected List<Observer> observers = new ArrayList<>(); public void add(Observer observer){ observers.add(observer); } public void remove(Observer observer){ observers.remove(observer); } protected abstract void notifyObserver(); }
具体目标:
public class ConcreteSubject extends Subject { @Override protected void notifyObserver() { observers.forEach(Observer::response); } }
抽象观察者:
public interface Observer { void response(); }
具体观察者:
public class ConcreteObserver1 implements Observer { @Override public void response() { System.out.println("concrete observer1 update state"); } } public class ConcreteObserver2 implements Observer { @Override public void response() { System.out.println("concrete observer2 update state"); } }
客户端:
public class Client { public static void main(String[] args) { Subject subject = new ConcreteSubject(); subject.add(new ConcreteObserver1()); subject.add(new ConcreteObserver2()); subject.notifyObserver(); /** * concrete observer1 update state * concrete observer2 update state */ } }
小结:
观察者模式应用很广泛,例如redis的发布订阅机制,消息中间件的消息传递,Reactor流式编程等。观察者模式中增加观察者不会影响原有的逻辑
18. 中介模式(Mediator)
定义一个中介对象封装一系列对象之间的交互,使原有对象至今耦合松散,并且可以独立改变他们之间的交互,又叫做调停模式
优点:
类之间各司其职,符合迪米特法则
降低对象之间耦合,使得对象可以被复用
对象之间一对多关联改为一对一关联,提升系统灵活性
缺点:
将原有的多个对象之间相互依赖编程中介者与多个同事类之间相互依赖,中介者有可能会变得臃肿,切难以维护
适用场景:
对象之间存在网状关系,依赖关系混乱且难以复用
中介模式UML:
代码如下:
抽象中介者:
public interface Mediator { void register(Colleague colleague); void relay(Colleague colleague); }
具体中介者:
public class ConcreteMediator implements Mediator { private final List<Colleague> colleagues = new ArrayList<>(); @Override public void register(Colleague colleague) { colleagues.add(colleague); colleague.setMediator(this); } @Override public void relay(Colleague colleague) { for (Colleague colleague1 : colleagues) { if (!colleague1.equals(colleague)) { colleague1.receive(); } } } }
抽象同事类:
public abstract class Colleague { protected Mediator mediator; public void setMediator(Mediator mediator) { this.mediator = mediator; } public abstract void receive(); public abstract void send(); }
具体同事类:
public class ConcreteColleague1 extends Colleague { @Override public void receive() { System.out.println("concrete colleague1 receive request"); } @Override public void send() { System.out.println("concrete colleague1 send request"); mediator.relay(this); } } public class ConcreteColleague2 extends Colleague { @Override public void receive() { System.out.println("concrete colleague2 receive request"); } @Override public void send() { System.out.println("concrete colleague2 send request"); mediator.relay(this); } }
客户端:
public class Client { public static void main(String[] args) { Mediator concreteMediator = new ConcreteMediator(); Colleague concreteColleague1 = new ConcreteColleague1(); Colleague concreteColleague2 = new ConcreteColleague2(); concreteMediator.register(concreteColleague1); concreteMediator.register(concreteColleague2); concreteColleague1.send(); concreteColleague2.send(); /** * concrete colleague1 send request * concrete colleague2 receive request * concrete colleague2 send request * concrete colleague1 receive request */ } }
小结:
中介者承担较多责任,因此实际使用时需要注意中介者逻辑问题。
减少了之间的依赖,由原来的网状结构分离为星状结构
19. 备忘录模式(Memento)
在不破坏封装性的前提下,捕捉一个对象的内部状态,并在对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。又称快照模式
优点:
- 提供一个恢复状态的机制
- 实现内部状态的封装
- 简化发起者,发起者不需要管理和保存内部状态,所有状态保存在备份中
缺点:
- 资源消耗大
适用场景:
- 需要保存与恢复数据的场景
- 需要一个回滚操作的场景
备忘录UML:
代码如下:
发起人:
public class Originator { private String state; public Originator(String state) { this.state = state; } public String getState() { return state; } public void setState(String state) { this.state = state; } public Memento createMemento() { return new Memento(state); } public void restoreMemento(Memento memento) { this.state = memento.getState(); } }
备忘录:
public class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return state; } public void setState(String state) { this.state = state; } }
管理者:
public class Caretaker { private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
客户类:
public class Client { public static void main(String[] args) { Originator originator = new Originator("1"); Caretaker caretaker = new Caretaker(); caretaker.setMemento(originator.createMemento()); System.out.println("origin stats: " + originator.getState()); originator.setState("change"); System.out.println("change stats: " + originator.getState()); originator.restoreMemento(caretaker.getMemento()); System.out.println("restore stats: " + originator.getState()); /** * origin stats: 1 * change stats: change * restore stats: 1 */ } }
小结:
如果类的成员变量很对哦,势必会占用较大的资源,并且每一次保存都会消耗一定的内存。
实现了内部状态的封装,用户无需关心保存细节,可以与原型模式配置使用。同时管理者可以根据业务维护一个快照集合,可以根据指定字段进行恢复
20. 解释器模式(Interpreter)
给定一个语言表达式,定义它文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子,这个解释器一般很少会用到
优点:
- 扩展性好,使用类来表示语言的文法规则,因此可以通过继承机制来改变或者扩展文法
- 容易实现,语法树中每个表达式节点类比较相似,实现文法较为容易
缺点:
- 执行效率低
- 引起类膨胀
- 可应用场景少
适用场景:
- 一些重复出现的问题,一个简单语法需要解释的场景例如编译器,运算表达式计算,正则表达式等
解释器模式UML:
角色:
- 抽象表达式类:定义解释器接口,用于约定解释器的解释操作
- 终结符表达式:抽象表达式的子类,实现文法中与终结符相关的操作
- 非终结符表达式:抽象表达式子类,实现文法中非终结符相关操作
- 环境:包含各个解释器需要的数据或者公共功能,一般用来传递被所有解释器共享的数据
使用解释器模式,实现一个简单的加法计算器
代码如下:
抽象表达式类:
public interface AbstractExpression { Object interpret(); }
终结符表达式类:
public class TerminalExpression implements AbstractExpression { public String info; public TerminalExpression(String info) { this.info = info; } @Override public Object interpret() { return this.info; } }
非终结符表达式类:
public class NonTerminalExpression implements AbstractExpression { private AbstractExpression exp1; private AbstractExpression exp2; public NonTerminalExpression(AbstractExpression exp1, AbstractExpression exp2) { this.exp1 = exp1; this.exp2 = exp2; } @Override public Object interpret() { return Integer.parseInt(this.exp1.interpret().toString()) + Integer.parseInt(this.exp2.interpret().toString()); } }
环境:
public class Context { private Stack<AbstractExpression> exps = new Stack<>(); public void operation(String info) { for (int i = 0; i < info.length(); i++) { char c = info.charAt(i); switch (c) { case '+': char right = info.charAt(++i); AbstractExpression terminalExpression = new NonTerminalExpression(exps.pop(), new TerminalExpression(String.valueOf(right))); exps.push(terminalExpression); break; default: exps.push(new TerminalExpression(String.valueOf(c))); } } AbstractExpression pop = exps.pop(); System.out.println("result==>: " + pop.interpret()); } }
客户端:
public class Client { public static void main(String[] args) { Context context = new Context(); context.operation("1+2+3"); /** * result==>: 6 */ } }
小结:
当有一个语言需要解释执行,可以将该语言中的句子表示为一个抽象语法树,考虑使用解释器模型,是程序具有良好的扩展性。但解释器会带埋一些问题,比如类膨胀,解释器模式采用递归方法,会导致调试苦难你,效率低下
21. 状态模式(State)
对有状态的对象,把复杂的判断逻辑提取到不同状态的对象中,允许独享在内部状态发生改变时改变其行为
优点:
结构清晰,状态模式与特定状态相关行为局部化到一个状态中,并将不用状态行为分割,满足单一职责原则
状态切换现实化,减少对象之间的相互依赖,将不同状态引入独立对象中会是的状态转变更加明确,减少对象之间的依赖
状态类职责明确,有利于程序扩展
缺点:
状态模式必然会是增加西永的类和对象的个数
使用不当,会造成代码的混乱
对于可以切换的状态模式,如果新增一种则需要修改源码,不满足开闭原则
适用场景:
一个对象的行为取决于它的状态,并且必须在运行是根据状态改变他的行为
一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态
状态模式UML:
代码如下:
抽象状态类:
public interface State { void handle(Context context); }
具体状态类:
public class ConcreteStateA implements State { @Override public void handle(Context context) { System.out.println("current state is A"); context.setState(new ConcreteStateB()); } } public class ConcreteStateB implements State { @Override public void handle(Context context) { System.out.println("current state is B"); context.setState(new ConcreteStateA()); } }
环境类:
public class Context { private State state; public Context(State state) { this.state = state; } public State getState() { return state; } public void setState(State state) { this.state = state; } public void handle() { state.handle(this); } }
客户端:
public class Client { public static void main(String[] args) { Context context = new Context(new ConcreteStateA()); context.handle(); context.handle(); context.handle(); context.handle(); /** * current state is A * current state is B * current state is A * current state is B */ } }
小结:
状态模式有很强的可读性,状态模式将每个状态行文封装在对应的类中。方便维护,但是会产生很多没,每个类都要有一个对应的类。我在我们工单业务场景中,由于工单=状态较多,并且不同工单状态对应不同的操作行为,当时有计划吧状态机的状态彻底分解为不同的类,后来考虑到业务暂时没有复杂到一定地步,因此暂时使用方法进行拆解,后续如业务继续扩展也会很方便拆解到状态模式。因此,选择的时候,要结合业务场景,避免过度设计。
22. 策略模式(Strategy)
定义算法簇,分别封装起来,然他们之间可以相关替换,策略模式让算法变化地理与使用算法的客户
优点:
- 避免多重条件语句
- 提供重用的算法族,恰当使用继承,将公共代码抓一只父类中
- 提供相同行为的不同实现
- 对开闭原则支持,可以在不修改代码情况下,灵活增加新算法
缺点:
- 客户端必须了解策略算法的区别
- 策略类较多,维护成本增加
适用场景:
- 一个类动态的在几种算法中选择一种是
- 系统中个算法彼此独立,并且要求客户隐藏具体算法实现细节
- 多个类只区别表现行为不同,运行时动态选择具体要执行的行为
策略模式UML:
代码如下:
抽象策略:
public interface Strategy { void strategyMethod(); }
具体策略:
public class ConcreteStrategyA implements Strategy { @Override public void strategyMethod() { System.out.println("concrete strategy A"); } } public class ConcreteStrategyB implements Strategy { @Override public void strategyMethod() { System.out.println("concrete strategy B"); } }
环境类:
public class Context { private Strategy strategy; public Context(Strategy strategy) { this.strategy = strategy; } public Strategy getStrategy() { return strategy; } public void setStrategy(Strategy strategy) { this.strategy = strategy; } public void strategyMethod() { strategy.strategyMethod(); } }
客户端:
public class Client { public static void main(String[] args) { Strategy strategyA = new ConcreteStrategyA(); Context context = new Context(strategyA); context.strategyMethod(); context.setStrategy(new ConcreteStrategyB()); context.strategyMethod(); /** * concrete strategy A * concrete strategy B */ } }
小结:
当策略系统中策略很多时,客户端管理的策略算法将会很复杂。
策略将变化的代码从不变的代码中分离出来
针对接口编程
多用组合、聚合,少用继承
23. 职责链模式(Chain Of Responsibility)
为了避免多个请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一个对象记住下一个对象的引用而连成的一条链。又称责任链
优点:
- 减低对象之间的耦合性。该模式是的一个对象无需知道到底哪个对象处理请求以及链的结构
- 增加系统的可扩展性。可以根据需求增加新的请求处理类,满足开闭原则
- 增加给对象指派职责的灵活性,可以动态的改变链中的成员或者次序
- 简化对象之间的连接,每个对象直选哟保存后继值得引用
- 单一职责,每个类只需要处理自己的工作
缺点:
- 如果责任链较长,可能会影响性能
- 责任链的搭建需要客户端保证,增加客户端的复杂性。如设置有问题,有可能会造成循环调用
适用场景:
- 多个对象处理一个请求,但具体处理请求可以由运行时决定
- 动态执行对象处理请求或者添加新的处理者
策略模式UML:
代码如下:
抽象处理者:
public abstract class Handler { private Handler next; public Handler getNext() { return next; } public void setNext(Handler next) { this.next = next; } public abstract void handleRequest(String request); }
具体处理者:
public class ConcreteHandler1 extends Handler { @Override public void handleRequest(String request) { System.out.println("I am concrete handle 1, handle -> " + request); if (Objects.nonNull(this.getNext())) { this.getNext().handleRequest(request); } else { System.out.println("this is the end 1"); } } } public class ConcreteHandler2 extends Handler { @Override public void handleRequest(String request) { System.out.println("I am concrete handle 2, handle -> " + request); if (Objects.nonNull(this.getNext())) { this.getNext().handleRequest(request); } else { System.out.println("this is the end 2"); } } }
public class ConcreteHandler1 extends Handler { @Override public void handleRequest(String request) { System.out.println("I am concrete handle 1, handle -> " + request); if (Objects.nonNull(this.getNext())) { this.getNext().handleRequest(request); } else { System.out.println("this is the end 1"); } } } public class ConcreteHandler2 extends Handler { @Override public void handleRequest(String request) { System.out.println("I am concrete handle 2, handle -> " + request); if (Objects.nonNull(this.getNext())) { this.getNext().handleRequest(request); } else { System.out.println("this is the end 2"); } } }
客户端:
public class Client { public static void main(String[] args) { Handler handler1 = new ConcreteHandler1(); Handler handler2 = new ConcreteHandler2(); handler1.setNext(handler2); handler1.handleRequest("request"); /** * I am concrete handle 1, handle -> request * I am concrete handle 2, handle -> request * this is the end 2 */ } }
小结:
将请求与处理分开,实现解耦,提高了系统的灵活性。责任的本质是解耦请求与处理,让请求在处理链中进行处理与处理。责任链
简化了对象,调试不方便,采用了类似递归方式,调试时逻辑可能比较复杂。spring中webflux的filter,netty的handler等很多经典的框架都用到了责任链模式。
结束语:
设计模式就是为了提升程序的代码重用性,可读性,可扩展性,可靠性,使程序呈现高内聚,低耦合的特性。但是也肯定会不可避免的增加一些类,或者占用内存,万事有利有弊,我们可以灵活的选择。当然做程序也要避免过度设计,不是为了技术而技术,用最简单的代码实现最复杂的逻辑才是所追求的。