行为模式是描述多个类与对象之间通过协作共同完成单个对象无法单独完成的任务。
行为模式分为:
行为模式:
定义一个操作中的算法骨架,将一些步骤延迟到子类中,使子类可以不改变一个算法的结构,子类可以重定义该算法的一些特定步骤
优点:
缺点:
适用场景:
模板方法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修饰。
将一个请求封装为一个对象,使请求责任与执行责任分隔开。将请求发送者与请求接受者消除彼此之间的耦合,是对象之间调用关系更加灵活,实现解耦。
优点:
缺点:
适用场景:
命令模式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 } }
小结:
请求调用者与请求接收者之间解耦是通过命令对象山西爱你,命令对象起到纽带桥梁作用。
可以设计一个命令队列,实现多线程执行命令
在进行设计时,可以增加空命令,省去判空的操作
封装一些作用于某种数据结构的各元素的操作,在不改变数据结构的前提下定义作用于这些元素的新操作。
优点:
缺点:
适用场景:
访问者模式UML
角色:
代码如下:
抽象元素:
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 */ } }
小结:
将数据的操作与结构进行分离,解决数据结构和操作耦合性,适用于数据结构稳定的系统
提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层标识,不暴露其内部的结构
优点:
缺点:
适用场景:
迭代器的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 */ } }
小结:
这里有一个设计思想就是一个类应当只有一个因七百年画的原型。在聚合类中,把迭代器分开,就是要把管理对象集合与便利对象集合的责任分开。
这个模式应当是非常熟悉的,对象之间存在多对一依赖的一种设计,被以来的对象叫做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流式编程等。观察者模式中增加观察者不会影响原有的逻辑
定义一个中介对象封装一系列对象之间的交互,使原有对象至今耦合松散,并且可以独立改变他们之间的交互,又叫做调停模式
优点:
类之间各司其职,符合迪米特法则
降低对象之间耦合,使得对象可以被复用
对象之间一对多关联改为一对一关联,提升系统灵活性
缺点:
将原有的多个对象之间相互依赖编程中介者与多个同事类之间相互依赖,中介者有可能会变得臃肿,切难以维护
适用场景:
对象之间存在网状关系,依赖关系混乱且难以复用
中介模式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 */ } }
小结:
中介者承担较多责任,因此实际使用时需要注意中介者逻辑问题。
减少了之间的依赖,由原来的网状结构分离为星状结构
在不破坏封装性的前提下,捕捉一个对象的内部状态,并在对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。又称快照模式
优点:
缺点:
适用场景:
备忘录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 */ } }
小结:
如果类的成员变量很对哦,势必会占用较大的资源,并且每一次保存都会消耗一定的内存。
实现了内部状态的封装,用户无需关心保存细节,可以与原型模式配置使用。同时管理者可以根据业务维护一个快照集合,可以根据指定字段进行恢复
给定一个语言表达式,定义它文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子,这个解释器一般很少会用到
优点:
缺点:
适用场景:
解释器模式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 */ } }
小结:
当有一个语言需要解释执行,可以将该语言中的句子表示为一个抽象语法树,考虑使用解释器模型,是程序具有良好的扩展性。但解释器会带埋一些问题,比如类膨胀,解释器模式采用递归方法,会导致调试苦难你,效率低下
对有状态的对象,把复杂的判断逻辑提取到不同状态的对象中,允许独享在内部状态发生改变时改变其行为
优点:
结构清晰,状态模式与特定状态相关行为局部化到一个状态中,并将不用状态行为分割,满足单一职责原则
状态切换现实化,减少对象之间的相互依赖,将不同状态引入独立对象中会是的状态转变更加明确,减少对象之间的依赖
状态类职责明确,有利于程序扩展
缺点:
状态模式必然会是增加西永的类和对象的个数
使用不当,会造成代码的混乱
对于可以切换的状态模式,如果新增一种则需要修改源码,不满足开闭原则
适用场景:
一个对象的行为取决于它的状态,并且必须在运行是根据状态改变他的行为
一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态
状态模式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 */ } }
小结:
状态模式有很强的可读性,状态模式将每个状态行文封装在对应的类中。方便维护,但是会产生很多没,每个类都要有一个对应的类。我在我们工单业务场景中,由于工单=状态较多,并且不同工单状态对应不同的操作行为,当时有计划吧状态机的状态彻底分解为不同的类,后来考虑到业务暂时没有复杂到一定地步,因此暂时使用方法进行拆解,后续如业务继续扩展也会很方便拆解到状态模式。因此,选择的时候,要结合业务场景,避免过度设计。
定义算法簇,分别封装起来,然他们之间可以相关替换,策略模式让算法变化地理与使用算法的客户
优点:
缺点:
适用场景:
策略模式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 */ } }
小结:
当策略系统中策略很多时,客户端管理的策略算法将会很复杂。
策略将变化的代码从不变的代码中分离出来
针对接口编程
多用组合、聚合,少用继承
为了避免多个请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一个对象记住下一个对象的引用而连成的一条链。又称责任链
优点:
缺点:
适用场景:
策略模式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等很多经典的框架都用到了责任链模式。
设计模式就是为了提升程序的代码重用性,可读性,可扩展性,可靠性,使程序呈现高内聚,低耦合的特性。但是也肯定会不可避免的增加一些类,或者占用内存,万事有利有弊,我们可以灵活的选择。当然做程序也要避免过度设计,不是为了技术而技术,用最简单的代码实现最复杂的逻辑才是所追求的。
原文:https://www.cnblogs.com/yangshixiong/p/14056537.html