【设计模式】设计模式——行为型
总览https://www.cnblogs.com/OhOfCourse/p/17163285.html
行为型,主要解决类或者对象之间互相通信的问题
注:从OneNote搬运过来,缺少一些UML图和代码示例。
一.责任链模式
1.0参考资料
1.1说明
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
1.2示例

1 抽象handler 2 abstract public class Handler 3 { 4 protected Handler successor;//指向下一个handler,像链表中的next 5 public void setSuccessor(Handler successor) 6 { 7 this.successor = successor; 8 } 9 abstract String handleRequest(String msg); 10 } 11 12 具体handler 13 public class HandlerA extends Handler { 14 @Override 15 String handleRequest(String msg) { 16 if(msg.contains("a")){ 17 msg = msg.replace('a', '*'); 18 } else if(this.successor != null){ 19 msg = this.successor.handleRequest(msg); 20 } 21 return msg; 22 } 23 } 24 public class HandlerB extends Handler { 25 @Override 26 String handleRequest(String msg) { 27 if(msg.contains("b")){ 28 msg = msg.replace('b', '*'); 29 } else if(this.successor != null){ 30 msg = this.successor.handleRequest(msg); 31 } 32 return msg; 33 } 34 } 35 public class HandlerC extends Handler { 36 @Override 37 String handleRequest(String msg) { 38 if(msg.contains("c")){ 39 msg = msg.replace('c', '*'); 40 } else if(this.successor != null){ 41 msg = this.successor.handleRequest(msg); 42 } 43 return msg; 44 } 45 } 46 47 客户端调用 48 public class Client { 49 public static void main(String[] args) { 50 Handler handlerA = new HandlerA(); 51 Handler handlerB = new HandlerB(); 52 Handler handlerC = new HandlerC(); 53 handlerA.setSuccessor(handlerB); 54 handlerB.setSuccessor(handlerC); 55 56 System.out.println(handlerA.handleRequest("apple")); 57 System.out.println(handlerA.handleRequest("bicycle")); 58 System.out.println(handlerA.handleRequest("color")); 59 } 60 }
1.3优缺点
二.命令模式
2.0参考资料
2.1说明
将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;
对请求排队或者记录请求日志,以及支持可撤销的操作
本质:将命令进行封装,将发出命令的责任和执行命令的责任分开。
角色
Command抽象命令:一般是一个抽象类或接口,其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关动作;
ConcreteCommand具体命令:对上实现抽象命令中的方法,对外将接收者对象的动作绑定进来,在实现的execute方法中调用接收者对象的相关操作Action
Receiver接收者:接收者执行与请求相关的操作,具体实现对请求业务的处理。
Invoker调用者:调用者即请求发送者,它通过命令对象来执行客户端的请求。在程序运行时可以将一个具体命令对象注入进来,再调用具体命令对象的execure方法, 间接实现调用接收者的相关操作。
2.2示例
2.3优缺点
三.解释器模式
3.0参考资料
3.1说明
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子
角色
Context是环境角色,包含解释器之外的一些全局信息;
AbstractExpression为抽象表达式,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享;
TerminalExression为终结符表达式,实现与文法中的终结符相关联的解释操作;
NonterminalExpression为非终结符表达式,为文法中的非终结符实现解释操作,对文法中每一条规则R1、R2……Rn都需要一个具体的非终结符表达式类。
3.2示例

1 public class Context { 2 private String input; 3 private String output; 4 public String getInput() { 5 return input; 6 } 7 public void setInput(String input) { 8 this.input = input; 9 } 10 public String getOutput() { 11 return output; 12 } 13 public void setOutput(String output) { 14 this.output = output; 15 } 16 } 17 18 //抽象表达式是生成语法集合(语法树)的关键,每个语法集合完成指定语法解析任务,它是通过递归调用的方式,最终由最小的语法单元进行解析完成 19 public abstract class AbstractExpression { 20 public abstract void Interpret(Context context); 21 } 22 23 //通常,终结符表达式比较简单,主要处理场景元素和数据的转换 24 public class TerminalExpression extends AbstractExpression { 25 @Override 26 public void Interpret(Context context) { 27 System.out.println("终端解释器"); 28 } 29 } 30 31 //每个非终结符表达式都代表了一个文法规则,并且每个文法规则都只关心自己周边的文法规则的结果,因此这就产生了每个非终结符表达式调用自己周边的非终结符表达式,然后最终、最小的文法规则就是终结符表达式 32 public class NonterminalExpression extends AbstractExpression { 33 @Override 34 public void Interpret(Context context) { 35 System.out.println("非终端解释器"); 36 } 37 } 38 39 //客户端 40 public class Client { 41 public static void main(String[] args) { 42 Context context = new Context(); 43 List<AbstractExpression> list = new ArrayList<>(); 44 45 list.add(new TerminalExpression()); 46 list.add(new NonterminalExpression()); 47 list.add(new TerminalExpression()); 48 list.add(new TerminalExpression()); 49 50 for (AbstractExpression abstractExpression : list) { 51 abstractExpression.Interpret(context); 52 } 53 } 54 }
3.3优缺点
四.迭代器模式
4.0参考资料
4.1说明
提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示
角色
Iterator抽象迭代器:定义访问和遍历元素的接口,声明了用于遍历数据元素的方法
ConcreteIterator:实现
Aggregate:抽象聚合类,用于存储和管理元素对象,声明一个构造迭代器的方法,充当抽象迭代器的工厂角色
ConcreteAggregate:实现
4.2示例

1 interface Iterator 2 { 3 String first(); 4 String next(); 5 boolean hasNext(); 6 String currentItem(); 7 } 8 9 interface Aggregate 10 { 11 Iterator createIterator(); 12 void add(String s); 13 } 14 15 class ConcreteAggregate implements Aggregate 16 { 17 List<String> list = new ArrayList<>(); 18 @Override 19 public Iterator createIterator() 20 { 21 return new ConcreteIterator(list); 22 } 23 @Override 24 public void add(String s) 25 { 26 list.add(s); 27 } 28 } 29 30 class ConcreteIterator implements Iterator 31 { 32 private int cursor; 33 private List<String> list; 34 public ConcreteIterator(List<String> list) 35 { 36 this.list = list; 37 this.cursor = -1; 38 } 39 @Override 40 public String first() 41 { 42 return list.size() > 0 ? 43 list.get(cursor = 0) : 44 null; 45 } 46 @Override 47 public String next() 48 { 49 return list.get( 50 cursor + 1 < list.size() ? ++cursor : cursor 51 ); 52 } 53 @Override 54 public boolean hasNext() 55 { 56 return cursor+1 < list.size(); 57 } 58 @Override 59 public String currentItem() 60 { 61 return list.get(cursor); 62 } 63 } 64 65 客户端 66 public static void main(String[] args) 67 { 68 Aggregate aggregate = new ConcreteAggregate(); 69 aggregate.add("111"); 70 aggregate.add("222"); 71 aggregate.add("jksdfjksdjkfk"); 72 aggregate.add("m,xcvm,xcm,v"); 73 Iterator iterator = aggregate.createIterator(); 74 while(iterator.hasNext()) 75 { 76 System.out.println(iterator.next()); 77 } 78 }
4.3优缺点
五.中介者模式
5.0参考资料
5.1说明
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用
角色
抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法
具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色
抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能
具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互
5.2示例

1 //抽象中介者 2 abstract class Mediator { 3 public abstract void register(Colleague colleague); 4 public abstract void relay(Colleague cl); //转发 5 } 6 //具体中介者 7 class ConcreteMediator extends Mediator { 8 private List<Colleague> colleagues = new ArrayList<Colleague>(); 9 public void register(Colleague colleague) { 10 if (!colleagues.contains(colleague)) { 11 colleagues.add(colleague); 12 colleague.setMedium(this);//将中介者放入业务类中 13 } 14 } 15 public void relay(Colleague cl) { 16 for (Colleague ob : colleagues) { 17 if (!ob.equals(cl)) { 18 ((Colleague) ob).receive();// 19 } 20 } 21 } 22 } 23 //抽象同事类 24 abstract class Colleague { 25 protected Mediator mediator; 26 public void setMedium(Mediator mediator) { 27 this.mediator = mediator; 28 } 29 public abstract void receive(); 30 public abstract void send(); 31 } 32 //具体同事类 33 class ConcreteColleague1 extends Colleague { 34 public void receive() { 35 System.out.println("具体同事类1收到请求。"); 36 } 37 public void send() { 38 System.out.println("具体同事类1发出请求。"); 39 mediator.relay(this); //请中介者转发 40 } 41 } 42 //具体同事类 43 class ConcreteColleague2 extends Colleague { 44 public void receive() { 45 System.out.println("具体同事类2收到请求。"); 46 } 47 public void send() { 48 System.out.println("具体同事类2发出请求。"); 49 mediator.relay(this); //请中介者转发 50 } 51 }
5.3优缺点
六.备忘录模式
6.0参考资料
https://www.cnblogs.com/adamjwh/p/11018268.html
6.1说明
(后悔药机制)
备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态
角色
Originator备忘录的记录对象,负责创建一个备忘录Memoto
Memoto备忘录角色,用于存储Originator的内部状态,并且可以防止Originator以外的对象访问Memoto
Caretaker备忘录管理员角色,负责存储备忘录,不能对备忘录的内容进行操作或检查
6.2示例

1 Originator 2 记录当前时刻的内部状态,并负责创建和恢复备忘录数据,允许访问返回到先前状态所需的所有数据 3 public class Originator { 4 private String state; 5 public String getState() { 6 return state; 7 } 8 public void setState(String state) { 9 this.state = state; 10 } 11 public Memento createMento() { 12 return (new Memento(state)); 13 } 14 public void setMemento(Memento memento) { 15 state = memento.getState(); 16 } 17 public void show() { 18 System.out.println("state = " + state); 19 } 20 } 21 22 Memoto 23 负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态 24 public class Memento { 25 private String state; 26 public Memento(String state) { 27 this.state = state; 28 } 29 public String getState() { 30 return state; 31 } 32 } 33 34 Caretaker 35 public class Caretaker { 36 private Memento memento;//也可用List来存储多个 37 public Memento getMemento() { 38 return memento; 39 } 40 public void setMemento(Memento memento) { 41 this.memento = memento; 42 } 43 } 44 45 客户端 46 public class Client { 47 public static void main(String[] args) { 48 Originator originator = new Originator(); 49 originator.setState("On"); //Originator初始状态 50 originator.show(); 51 52 Caretaker caretaker = new Caretaker(); 53 caretaker.setMemento(originator.createMento()); 54 55 originator.setState("Off"); //Originator状态变为Off 56 originator.show(); 57 58 originator.setMemento(caretaker.getMemento()); //回复初始状态 59 originator.show(); 60 } 61 }
6.3优缺点
七.观察者模式
7.0参考资料
7.1说明
概要
用于建立一种对象之间的依赖关系,当一个对象发生改变时自动通知其他对象,其他对象将做出反应。
一个观察目标可以对应多个观察者,观察者之间没有相互关联,可以根据需要增加和删除观察者。
适用场景
(1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在一个独立的对象中使它们可以独立地改变和复用
(2)一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象发生改变,也不知道这些对象是谁
需要在系统中创建一个触发链,A对象的行为会影响B对象,B对象的行为会影响C对象,可以使用观察者模式创建一种链式触发机制
角色
(1)Subejct(抽象目标):又叫主题,指被观察的对象,也就是被观察者,在目标中定义了一个观察者集合,同时提供一系列方法来增加或者删除观察者对象,也定义了通知方法notify
(2)ConcreteSubject(具体目标):抽象目标的子类,通常包含有经常改变的数据,当状态发生改变时,向各个观察者发出通知,同时还实现了目标类中定义的抽象业务逻辑,如果无须扩展抽象目标类则可以省略具体目标类
(3)Observer(抽象观察者):对观察目标作出响应,一般定义为接口
ConcreteObserver(具体观察者):具体观察者中维护一个指向具体目标的引用,存储具体观察者的有关状态,这些状态需要与具体目标的状态保持一致,同时实现了抽象观察者的update方法
7.2示例

1 interface Observer 2 { 3 void update(String state); 4 } 5 6 class ConcreteObserver implements Observer 7 { 8 public String state; 9 public ConcreteObserver(String state) 10 { 11 this.state = state; 12 } 13 @Override 14 public void update(String state) 15 { 16 System.out.println("观察者状态更新为"+state); 17 } 18 } 19 20 abstract class Subject 21 { 22 private List<Observer> list = new ArrayList<>(); 23 public void attach(Observer observer) 24 { 25 list.add(observer); 26 } 27 public void detach(Observer observer) 28 { 29 list.remove(observer); 30 } 31 public void notifyObservers(String state) 32 { 33 list.forEach(t->t.update(state)); 34 } 35 public abstract void change(String newState); 36 } 37 38 class ConcreteSubject extends Subject 39 { 40 private String state; 41 public String getState() 42 { 43 return state; 44 } 45 @Override 46 public void change(String newState) 47 { 48 state = newState; 49 System.out.println("被观察者状态为:"+newState); 50 notifyObservers(newState); 51 } 52 } 53 客户端调用 54 public static void main(String[] args) 55 { 56 Observer observer1 = new ConcreteObserver("111"); 57 Observer observer2 = new ConcreteObserver("111"); 58 Observer observer3 = new ConcreteObserver("111"); 59 Subject subject = new ConcreteSubject(); 60 subject.attach(observer1); 61 subject.attach(observer2); 62 subject.attach(observer3); 63 subject.change("2222"); 64 }
7.3优缺点
优点
(1)分离表示层与逻辑层:定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色
(2)降低耦合:在抽象目标以及抽象观察者之间建立了一个抽象耦合,观察目标只需要维持一个抽象观察者的集合,无须了解具体观察者,由于观察目标和观察者没有紧密耦合在一起,因此它们可以属于不同的抽象层次
(3)广播:观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多的系统设计难度
(4)满足OCP:观察者模式满足开放闭合原则的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者之间与观察目标之间不存在关联关系的情况下,增加新的观察目标很方便
缺点
(1)通知费时:如果有很多观察者,通知需要耗费较多时间
(2)循环依赖导致崩溃:如果观察者模式与观察目标之间存在循环依赖,观察目标会导致触发它们之间进行循环调用,可能导致崩溃
(3)不明确变化内容:观察者模式只是让观察者知道观察目标发生了变化,但是不知道变化的内容是什么
八.策略模式
8.0参考资料
8.1说明
它定义了算法家族,分别封装起来,让他们之间可以可以互相替换,此模式让算法的变化,不会影响到使用算法的客户
8.2示例

1 // Strategy pattern -- Structural example 2 using System; 3 4 // "Strategy" 5 abstract class Strategy 6 { 7 // Methods 8 abstract public void AlgorithmInterface(); 9 } 10 11 // "ConcreteStrategyA" 12 class ConcreteStrategyA : Strategy 13 { 14 // Methods 15 override public void AlgorithmInterface() 16 { 17 Console.WriteLine("Called ConcreteStrategyA.AlgorithmInterface()"); 18 } 19 } 20 21 // "ConcreteStrategyB" 22 class ConcreteStrategyB : Strategy 23 { 24 // Methods 25 override public void AlgorithmInterface() 26 { 27 Console.WriteLine("Called ConcreteStrategyB.AlgorithmInterface()"); 28 } 29 } 30 31 // "ConcreteStrategyC" 32 class ConcreteStrategyC : Strategy 33 { 34 // Methods 35 override public void AlgorithmInterface() 36 { 37 Console.WriteLine("Called ConcreteStrategyC.AlgorithmInterface()"); 38 } 39 } 40 41 // "Context" 42 class Context 43 { 44 // Fields 45 Strategy strategy; 46 47 // Constructors 48 public Context( Strategy strategy ) 49 { 50 this.strategy = strategy; 51 } 52 53 // Methods 54 public void ContextInterface() 55 { 56 strategy.AlgorithmInterface(); 57 } 58 } 59 60 /// <summary> 61 /// Client test 62 /// </summary> 63 public class Client 64 { 65 public static void Main( string[] args ) 66 { 67 // Three contexts following different strategies 68 Context c = new Context( new ConcreteStrategyA() ); 69 c.ContextInterface(); 70 71 Context d = new Context( new ConcreteStrategyB() ); 72 d.ContextInterface(); 73 74 Context e = new Context( new ConcreteStrategyC() ); 75 e.ContextInterface(); 76 } 77 }
8.3优缺点
优点
(1)策略类之间可以自由切换。由于策略类都实现同一个接口,所以使它们之间可以自由切换。
(2)易于扩展。增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码。
(3)避免使用多重条件选择语句,充分体现面向对象设计思想。
缺点
(1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这点可以考虑使用IOC容器和依赖注入的方式来解决,关于IOC容器和依赖注入(Dependency Inject)的文章可以参考:IoC 容器和Dependency Injection 模式。
策略模式会造成很多的策略类
九.状态模式
9.0参考资料
9.1说明
定义
允许一个对象在其内部状态修改时改变它的行为,对象看起来似乎修改了它的类。
第一部分的意思是将状态封装为独立的类,并将请求委托给当前的对象,当对象内部状态变化时会带来不同行为的变化。
第二部分是从客户端角度看,使用的对象在不同的状态下有截然不同的行为。这个对对象看起来就像是从不同的类实例化而来,实际上这是使用了委托的效果。
适用
主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为之间是一一对应的,状态之间可以互相转换。
角色
(1)Context类为环境角色,用于维护State实例,这个实例定义当前状态;
(2)State是抽象状态角色,定义一系列接口,对应于Context提供的代理接口;
(3)ConcreteState是具体的状态角色,每个子类实现一个与Context的一个状态相关的行为;
9.2示例

1 interface State 2 { 3 void handle(Context context); 4 } 5 6 class Context 7 { 8 private State state = new ConcreteState1(); 9 public void setState(State state) 10 { 11 this.state = state; 12 } 13 public void request() 14 { 15 state.handle(this); 16 } 17 } 18 19 class ConcreteState1 implements State 20 { 21 @Override 22 public void handle(Context context) 23 { 24 System.out.println("具体状态1方法"); 25 context.setState(new ConcreteState2());//在这里负责切换下一个状态 26 } 27 } 28 class ConcreteState2 implements State 29 { 30 @Override 31 public void handle(Context context) 32 { 33 System.out.println("具体状态2方法"); 34 context.setState(new ConcreteState1());//在这里负责切换下一个状态 35 } 36 } 37 38 客户端 39 public static void main(String[] args) 40 { 41 Context context = new Context(); 42 context.request(); 43 context.request(); 44 }
9.3优缺点
十.模板方法模式
10.0参考资料
https://zhuanlan.zhihu.com/p/54359009
10.1说明
含义
定义一个操作中的算法框架,而将一些步骤延迟到子类中。使子类可以不改变一个算法的结构即可重定义该算法的某些步骤。
依托规则
抽象类中的抽象方法必须由子类实现,
抽象类中可以有非抽象方法作为模板方法,在非抽象方法中对抽象方法按照一定的逻辑进行调用,即算法的逻辑
实现方式
在模板类中定义一系列抽象方法等待子类实现,而又在非抽象方法中定义算法的步骤
10.2示例
10.3优缺点
优点
(1)封装不变的部分,扩展可变的部分。把认为是不变的部分的算法封装到父类,可变部分的交由子类来实现;
(2)提取公共部分的代码,行为由父类控制,子类实现;
缺点
(1)抽象类定义了部分抽象方法,这些抽象的方法由子类来实现,子类执行的结果影响了父类的结果(子类对父类产生了影响),会带来阅读代码的难度
十一.访问者模式
11.0参考资料
11.1说明
暂略
11.2示例
11.3优缺点
本文作者:啊原来是这样呀
本文链接:https://www.cnblogs.com/OhOfCourse/p/17163365.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步