设计模式-可复用面向对象软件设计的基础-行为模式
行为模式是通过将多个类通过继承\组合的形式形成对方法的处理,主要有以下几种
- 责任链模式
- 命令模式
- 解释器模式
- 迭代器模式
- 中介者模式
- 备忘录模式
- 观察者模式
- 状态模式
- 策略模式
- 模板模式
- 访问者模式
责任链模式
意图
责任链模式是使得多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,这些对象的处理过程逻辑上形成链状结构,请求沿着这条链依次被不同的对象进行处理。标准的定义是直到有一个对象处理请求为止,我自己理解应该是依次进行处理,而不是有一个处理即止,因为如果只有一个处理,那么责任链模式与策略模式类似。责任链模式是通过将请求需要依次处理的场景从显的客户端一个一个调用的过程,升级成了将请求传入和由责任链客户端去串联整个调用过程,并且这样更容易扩展步骤。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
结构
实例
- AbstractLogger.java
public abstract class AbstractLogger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
//责任链中的下一个元素
protected AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger){
this.nextLogger = nextLogger;
}
public void logMessage(int level, String message){
if(this.level <= level){
write(message);
}
if(nextLogger !=null){
nextLogger.logMessage(level, message);
}
}
abstract protected void write(String message);
}
- ChainPatternDemo.java
public class ChainPatternDemo {
private static AbstractLogger getChainOfLoggers(){
AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);
return errorLogger;
}
public static void main(String[] args) {
AbstractLogger loggerChain = getChainOfLoggers();
loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
loggerChain.logMessage(AbstractLogger.DEBUG,
"This is a debug level information.");
loggerChain.logMessage(AbstractLogger.ERROR,
"This is an error information.");
}
}
ErrorLogger、FileLogger、ConsoleLogger都是AbstractLogger.java的子类,在客户端中,通过提供setNextHandler的方法,客户端可以根据业务场景自由组合链的顺序
命令模式
意图
将请求封装成为一个对象,从而使得你可以用不同的请求对客户进行参数化
动机
命令模式主要是将请求和对请求执行的动作责任分离,让两者都可以独立进行演化。适合请求内容是类似于信号量的场景,接受者可以根据这个请求内容进行不同的处理
结构
实例
- Command
public interface Command {
/**
* 执行方法
*/
public void execute();
}
Command.java 定义的抽象命令类,所有具体命令的接口
- PlayCommand.java
public class PlayCommand implements Command {
//具体的执行类
private AudioPlayer myAudio;
public PlayCommand(AudioPlayer audioPlayer){
myAudio = audioPlayer;
}
/**
* 执行方法
*/
@Override
public void execute() {
myAudio.play();
}
}
PlayCommand是具体的命令执行类,内部具有一个execute方法,该方法会执行该命令定义的动作
- Keypad.java
public class Keypad {
private Command playCommand;
public void setPlayCommand(Command playCommand) {
this.playCommand = playCommand;
}
//执行播放方法
public void play(){
playCommand.execute();
}
}
keyPad作为请求类,再次对命令进行一次封装,便于客户端进行调用,这里可以不用再次封装,自己通过客户端进行调用
- client.java
public class Julia {
public static void main(String[]args){
//创建接收者对象
AudioPlayer audioPlayer = new AudioPlayer();
//创建命令对象
Command playCommand = new PlayCommand(audioPlayer)
//创建请求者对象
Keypad keypad = new Keypad();
keypad.setPlayCommand(playCommand);
//执行具体的类
keypad.play()
}
}
客户端通过调用请求类==Keypad==的指定方法,keyPad会调用具体的命令封装类进行执行。其实命令模式只是将请求和对应的动作进行分离,便于复杂请求场景的扩展
解释器模式
意图
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
动机
给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
结构
迭代器模式
意图
提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露聚合对象内部表示的模式
别名
游标(Cursor)
动机
迭代器模式是为了将内部元素和访问/遍历动作分离开,通过增加一个迭代器,通过迭代器来访问内部元素。
结构
中介者模式
意图
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互
动机
由于面向对象鼓励按照行为进行类的划分,因此系统中会存在大量的类。如果不同的类之间都要相互引用来完成一个行为的话不符合面向对象的特性,因此需要有一个中介来将服务聚合起来,相当于将细小的行为进行一次聚合形成较大的行为,这样不用关注细小的行为对象。
结构
中介者模式在功能上类似于门面模式,但是门面模式着重于对子系统类的封装,中介者模式着重是对行为的封装和调和各个子行为从而形成一个大的行为,因此门面模式是结构型而中介者模式是行为型。
备忘录模式
意图
在不破坏对象封装性的前提下,捕获一个对象的内部状态,并将该状态保存在对象之外。在需要时,可以根据这个数据进行恢复。类似于游戏中的存档点设置。
别名
token
动机
备忘录模式针对于那些需要进行暂存的数据或对象,在不破坏封装性的前提下。
结构
CareTaker.java 负责保存每个阶段的对象状态
观察者模式
意图
定义对象之间的一种一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都会得到通知
别名
依赖(Dependents),发布-订阅(Publish-Subscribe)
动机
在一个系统中一个对象的改变会导致引起相关对象的改变,如果要保持这样的一致性会导致对象间强依赖。观察者模式就是通过发布-订阅模式将通知的职责从动作类中强制依赖转换为通知的模式
结构
Subject作为被观察者中维护一个所有观察者的引用,Observer作为被观察者维护一个Subject的引用,将自己组成到被观察者中
可参考
观察者模式
实例
spring中的ApplicationListener和ApplicationEvent、ApplicationEventPublisherAware分别作为被观察者、观察者和客户端
状态模式
意图
允许一个对象在其内部状态发生改变时改变它的行为。从表象上来看就是对象可以根据内部状态执行不同的业务逻辑。
动机
一个对象的行为取决于内部状态进行驱动时,状态模式可以将状态和行为分离,减少分支的条件语句。
结构
可以看到doAction()方法中才会执行具体的行为,不同的状态具有不同的行为,通过context维护状态,当状态发生改变时,对应的行为也随之发生改变
策略模式
意图
策略模式是将不同的算法单独定义起来,通过不同的场景选择不同的算法。将客户端中的条件分支去掉,并且支持扩展。
结构
Context中的strategy类是具体执行的算法,这里有两种设计方案,第一种就是UML图中的写法,将Contetxx作为每次行动的容器,每次执行前都先进行赋值。第二种是Context持有一个Strategy的List根据添加选择算法、
策略模式在实际工作中大量使用到,因为消除了条件判断减少了分支。通过增加类的方式来减少分支,结构上便于扩展了
模板模式
意图
定义一个方法的骨架,通过继承的方式让子类可以改变特点行为,使得不用重新定义算法的顺序就能改变行为。
动机
模板方法的出现主要是为了解决重复定义的算法执行顺序并且具体执行有差异的场景
结构
模板模式与策略模式的区别在于模板模式是通过继承的方式来改变行为,策略模式是通过改变委托对象的方式来改变行为;策略模式针对的场景是相同类在处理不同的业务场景时候算法的选择问题,模板模式针对的是相同的类在处理相同的业务场景下算法细微的差异结构上。
访问者模式
意图
将数据和数据操作分离
动机
对一个对象需要进行多次操作时,为了避免这些操作将对象污染,可以通过访问者模式将数据和操作进行隔离。实现不同的访问者访问不同的数据。
结构
访问者模式的核心是在于数据对象会针对不同的访问者定义出不同的行为,由于访问者知道被访问的数据对象中的数据结构,因此访问者可以根据不同的对象定义出不同的行为
访问者的好处在于将数据对象的操作延迟到的访问者那一步中去了,通过定义不同的访问者可以执行不同的动作。