设计模式
19、命令模式的概念及Java实现
命令(Command)模式指将请求封装为命令基于事件驱动异步地执行,以实现命令的发送者和命令的执行者之间的解耦,提高命令发送、执行的效率和灵活度。
命令模式将命令调用者与命令执行者解耦,有效降低系统的耦合度。同时,由于命令调用者和命令执行者进行了解耦,所以增加和删除(回滚)命令变得非常方便。
命令模式包含以下主要角色。
- 抽象命令类(Command):执行命令的接口,定义执行命令的抽象方法execute()。
- 具体命令类(Concrete Command):抽象命令类的实现类,持有接收者对象,并在接收到命令后调用命令执行者的方法action()实现命令的调用和执行。
- 命令执行者(Receiver):命令的具体执行者,定义了命令执行的具体方法action()。
- 命令调用者(Invoker):接收客户端的命令并异步执行。
具体的UML设计如图所示。

具体实现如下。
(1)定义Command接口:
java
public interface Command {
public void exe(String command);
}
以上代码定义了Command接口,并在该接口中定义了Command的执行方法exe()。
(2)定义Command接口的实现类ConcreteCommand:
java
public class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void exe(String command) {
receiver.action(command);
}
}
以上代码定义了Command接口的实现类ConcreteCommand,该类持有命令接收和执行者Receiver的实例,并实现了Command接口中的exe(),具体操作是在ConcreteCommand接收到命令后,调用Receiver的action()将命令交给Receiver执行。
(3)定义命令调用者类Invoker:
java
public class Invoker {
private final static Log logger = LogFactory.getLog(Invoker.class);
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void action(String commandMessage){
logger.info("command sending...");
command.exe(commandMessage);
}
}
以上代码定义了命令调用者类Invoker,该类持有Command实例并在action()中实现了对命令的调用,具体做法是在action()中执行Command的exe()。
(4)定义命令的接收和执行者类Receiver:
java
public class Receiver {
private final static Log logger = LogFactory.getLog(Receiver.class);
public void action(String command){//接收并执行命令
logger.info("command received, now execute command");
}
}
以上代码定义了命令的接收和执行者类Receiver,并在action()中接收和执行命令。
(5)使用命令模式:
java
public static void main(String[] args) {
//定义命令的接收和执行者
Receiver receiver = new Receiver();
//定义命令实现类
Command cmd = new ConcreteCommand(receiver);
//定义命令调用者
Invoker invoker = new Invoker(cmd);
//命令调用
invoker.action("command1");
}
在使用命令模式时首先要定义一个命令接收和执行者Receiver,接着定义一个具体的命令ConcreteCommand实例,并将命令接收者实例设置到实例中,然后定义一个命令的调用者Invoker实例,并将命令实例设置到实例中,最后调用命令调用者的action(),将命令发送出去,在命令接收者收到数据后会执行相关命令,这样就完成了命令的调用。具体的执行结果如下:
java
[INFO] Invoker - command sending...
[INFO] Receiver - command received, now execute command
20、备忘录模式的概念及Java实现
备忘录(Memento)模式又叫作快照模式,该模式将当前对象的内部状态保存到备忘录中,以便在需要时能将该对象的状态恢复到原先保存的状态。
备忘录模式提供了一种保存和恢复状态的机制,常用于快照的记录和状态的存储,在系统发生故障或数据发生不一致时能够方便地将数据恢复到某个历史状态。
备忘录模式的核心是设计备忘录类及用于管理备忘录的管理者类,其主要角色如下。
- 发起人(Originator):记录当前时刻对象的内部状态,定义创建备忘录和恢复备忘录数据的方法。
- 备忘录(Memento):负责存储对象的内部状态。
- 状态管理者(Storage):对备忘录的历史状态进行存储,定义了保存和获取备忘录状态的功能。注意,备忘录只能被保存或恢复,不能进行修改。
具体的UML设计如图所示。

具体实现如下。
(1)定义原始数据Original:
java
public class Original {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Original(String value) {
this.value = value;
}
public Memento createMemento(){
return new Memento(value);
}
public void restoreMemento(Memento memento){
this.value = memento.getValue();
}
}
以上代码定义了原始数据Original,在原始数据中定义了createMemento()和restoreMemento()分别用于创建备忘录和从备忘录中恢复数据。
(2)定义备忘录Memento:
java
public class Memento {
private String value;
public Memento(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
以上代码定义了备忘录Memento,其中value为备忘录具体的数据内容。
(3)定义备忘录管理者Storage:
java
public class Storage {
private Memento memento;
public Storage(Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
以上代码定义了备忘录管理者Storage,持有备忘录实例,并提供了setMemento()和getMemento()分别用来设置和获取一个备忘录数据。
(4)使用备忘录:
java
public static void main(String[] args) {
//创建原始类
Original original = new Original("张三");
//创建备忘录
Storage storage = new Storage(original.createMemento());
//修改原始类的状态
logger.info("original value:" + original.getValue());
original.setValue("李四");
logger.info("update value:" + original.getValue());
//恢复原始类的状态
original.restoreMemento(storage.getMemento());
logger.info("restore value:" + original.getValue());
}
备忘录的使用方法比较简单:先定义一个原始数据,然后将数据存储到Storage,这时我们可以修改数据,在我们想把数据回滚到之前的状态时调用Original的restoreMemento()便可将存储在Storage中上次数据的状态恢复。其实,备忘录简单来说就是把原始数据的状态在Storage中又重新存储一份,在需要时可以恢复数据。
上面的例子只存储了数据的上一次状态,如果想存储多个状态,就可以在Storage中使用列表记录多个状态的数据。具体的执行结果如下:
java
[INFO] MementoDemo - original value:张三
[INFO] MementoDemo - update value:李四
[INFO] MementoDemo - restore value:张三
21、状态模式的概念及Java实现
状态模式指给对象定义不同的状态,并为不同的状态定义不同的行为,在对象的状态发生变换时自动切换状态的行为。
状态模式是一种对象行为型模式,它将对象的不同行为封装到不同的状态中,遵循了"单一职责"原则。同时,状态模式基于对象的状态将对象行为进行了明确的界定,减少了对象行为之间的相互依赖,方便系统的扩展和维护。
状态模式在生活中很常见,比如日常生活有工作状态、休假状态;钉钉有出差、会议、工作中等状态。每种状态都对应不同的操作,比如工作状态对应的行为有开会、写PPT、写代码、做设计等,休假状态对应的行为有旅游、休息、陪孩子等。
状态模式把受环境改变的对象行为包装在不同的状态对象里,用于让一个对象在其内部状态改变时,行为也随之改变。具体的角色如下。
- 环境(Context):也叫作上下文,用于维护对象当前的状态,并在对象状态发生变化时触发对象行为的变化。
- 抽象状态(AbstractState):定义了一个接口,用于定义对象中不同状态所对应的行为。
- 具体状态(Concrete State):实现抽象状态所定义的行为。
下面以工作状态、休假状态及两种状态下不同的行为为例介绍状态模式。具体的UML设计如图所示。

具体实现如下。
(1)定义AbstractState:
java
public abstract class AbstractState {
public abstract void action(Context context);
}
以上代码定义了AbstractState抽象类,在类中定义了action()用于针对不同的状态执行不同的动作。
(2)定义AbstractState的子类HolidayState:
java
public class HolidayState extends AbstractState {
private final static Log logger = LogFactory.getLog(HolidayState.class);
public void action(Context context) {
logger.info("state change to holiday state ");
logger.info("holiday state actions is travel,
shopping, watch television...");
}
}
以上代码定义了AbstractState的子类HolidayState并实现了action(), HolidayState中的action()的主要动作是旅行(travel)、购物(shopping)、看电视(watchtelevision)等。
(3)定义AbstractState的子类WorkState:
java
public class WorkState extends AbstractState {
private final static Log logger = LogFactory.getLog(WorkState.class);
public void action(Context context) {
logger.info("state change to work state ");
logger.info("work state actions is meeting, design, coding...");
}
}
以上代码定义了AbstractState的子类WorkState并实现了action(), WorkState中action()的主要动作是开会(meeting)、设计(design)、写代码(coding)等。
(4)定义Context用于存储状态和执行不同状态下的行为:
java
public class Context {
private AbstractState state;
public Context(AbstractState state){
this.state = state;
}
public void setState(AbstractState state){
this.state = state;
}
public AbstractState getState(){
return state;
}
public void action()
{
this.state.action(this);
}
}
以上代码定义了Context类,该类用于设置上下文环境中的状态,并根据不同的状态执行不同的action()。这里状态的设置通过setState()完成,具体的动作执行通过action()完成。
(5)使用状态模式:
java
public static void main(String[] args) {
//定义当前状态为工作状态
Context context = new Context(new WorkState());
context.action();
//切换当前状态为修改状态
context.setState(new HolidayState());
context.action();
}
在使用状态模式时,只需定义一个上下文Context,并设置Context中的状态,然后调用Context中的行为方法即可。以上代码首先通过Context的构造函数将状态设置为WorkState,接着通过setState()将状态设置为HolidayState,两种不同的状态将对应不同的行为,具体的执行结果如下:
java
[INFO] WorkState - state change to work state
[INFO] WorkState - work state actions is meeting, design, coding...
[INFO] HolidayState - state change to holiday state
[INFO] HolidayState - holiday state actions is travel, shopping, watch television...
22、访问者模式的概念及Java实现
访问者(Visitor)模式指将数据结构和对数据的操作分离开来,使其在不改变数据结构的前提下动态添加作用于这些元素上的操作。它将数据结构的定义和数据操作的定义分离开来,符合"单一职责"原则。访问者模式通过定义不同的访问者实现对数据的不同操作,因此在需要给数据添加新的操作时只需为其定义一个新的访问者即可。
访问者模式是一种对象行为型模式,主要特点是将数据结构和作用于结构上的操作解耦,使得集合的操作可自由地演化而不影响其数据结构。它适用于数据结构稳定但是数据操作方式多变的系统中。
访问者模式实现的关键是将作用于元素的操作分离出来封装成独立的类,包含以下主要角色。
- 抽象访问者(Visitor):定义了一个访问元素的接口,为每类元素都定义了一个访问操作visit(),该操作中的参数类型对应被访问元素的数据类型。
- 具体访问者(ConcreteVisitor):抽象访问者的实现类,实现了不同访问者访问到元素后具体的操作行为。
- 抽象元素(Element):元素的抽象表示,定义了访问该元素的入口的accept()方法,不同的访问者类型代表不同的访问者。
- 具体元素(Concrete Element):实现抽象元素定义的accept()操作,并根据访问者的不同类型实现不同的业务逻辑。
比如,我们有个项目计划需要上报,项目计划的数据结构是稳定的,包含项目名称和项目内容,但项目的访问者有多个,比如项目经理、CEO和CTO。类似的数据结构稳定但对数据的操作多变的情况很适合只用访问者模式实现。下面以项目的访问为例介绍访问者模式。具体的UML设计如图所示。

具体实现如下。
(1)定义抽象Visitor接口:
java
public interface Visitor {
void visit(ProjectElement element);
}
以上代码定义了Visitor接口,并在接口中定义了visit()用于指定要访问的数据。
(2)定义Visitor实现类CEOVisitor:
java
public class CEOVisitor implements Visitor{
private final static Log logger = LogFactory.getLog(CEOVisitor.class);
@Override
public void visit(ProjectElement element) {
logger.info("CEO Visitor Element");
element.signature("CEO", new Date());
logger.info(JSON.toJSON(element));
}
}
以上代码定义了Visitor实现类CEOVisitor,并实现了其方法visit(),该方法在接收到具体的元素时,访问该元素并调用signature()签名方法表示CEOVisitor已经访问和审阅了该项目。
(3)定义Visitor实现类CTOVisitor:
java
public class CTOVisitor implements Visitor {
private final static Log logger = LogFactory.getLog(CEOVisitor.class);
@Override
public void visit(ProjectElement element) {
logger.info("CTO Visitor Element");
element.signature("CTO", new Date());
logger.info(JSON.toJSON(element));
}
}
以上代码定义了Visitor实现类CTOVisitor,并实现了其方法visit(),该方法在接收到具体的元素时,会访问该元素并调用signature()签名方法表示CTOVisitor已经访问和审阅了该项目。
(4)定义抽象元素Element的接口:
java
public interface Element {
void accept(Visitor visitor);
}
以上代码定义了抽象元素Element,并定义了accept()用于接收访问者对象。
(5)定义具体元素ProjectElement的类:
java
public class ProjectElement implements Element {
private String projectName ;
private String projectContent ;
private String visitorName ;
private Date visitorTime ;
public ProjectElement(String projectName, String projectContent) {
this.projectName = projectName;
this.projectContent = projectContent;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void signature(String visitorName, Date visitorTime) {
this.visitorName = visitorName;
this.visitorTime = visitorTime;
}
//省略getter、setter
以上代码定义了ProjectElement用于表示一个具体的元素,该元素表示一个项目信息,包含项目名称projectName、项目内容projectContent、项目访问者visitorName和项目访问时间,还定义了signature()用于记录访问者的签名,以及accept()用于接收具体的访问者。
(6)使用访问者模式:
java
public static void main(String[] args) {
Element element = new ProjectElement("mobike", "share bicycle");
element.accept(new CTOVisitor());
element.accept(new CEOVisitor());
}
在使用访问者模式时,首先需要定义一个具体的元素,然后通过accept()为元素添加访问者即可。具体的执行结果如下:
java
[INFO] CEOVisitor - CTO Visitor Element
[INFO] CEOVisitor - {"projectContent":"share bicycle",
"visitorName":"CTO", "visitorTime":1557370801734, "projectName":"mobike"}
[INFO] CEOVisitor - CEO Visitor Element
[INFO] CEOVisitor - {"projectContent":"share bicycle",
"visitorName":"CEO", "visitorTime":1557370801888, "projectName":"mobike"}
23、中介者模式的概念及Java实现
中介者(Mediator)模式指对象和对象之间不直接交互,而是通过一个名为中介者的角色来实现对象之间的交互,使原有对象之间的关系变得松散,且可以通过定义不同的中介者来改变它们之间的交互。中介者模式又叫作调停模式,是迪米特法则的典型应用。
中介者模式属于对象行为型模式,其主要特点是将对象与对象之间的关系变为对象和中介者之间的关系,降低了对象之间的耦合性,提高了对象功能的复用性和系统的灵活性,使得系统易于维护和扩展。
中介者模式包含以下主要角色。
- 抽象中介者(Mediator):中介者接口,定义了注册同事对象方法和转发同事对象信息的方法。
- 具体中介者(Concrete Mediator):中介者接口的实现类,定义了一个List来保存同事对象,协调各个同事角色之间的交互关系。
- 抽象同事类(Colleague):定义同事类的接口,持有中介者对象,并定义同事对象交互的抽象方法,同时实现同事类的公共方法和功能。
- 具体同事类(Concrete Colleague):抽象同事类的实现者,在需要与其他同事对象交互时,通过中介者对象来完成。
下面以租房场景为例来介绍中介者模式。我们知道,在租房时会找房屋中介,把自己的租房需求告知中介,中介再把需求告知房东,在整个过程中租房者和房东不产生直接关系(也不能产生关系,不然中介就没钱赚了),而是通过中介来完成信息交互,这样就完成了对象之间的解耦,也就是租户和房东的解耦,房东不用关心具体有哪些房客、房客有哪些需求,租房者也不用辛苦寻找房东及房子的信息。具体的UML设计如图所示。

具体实现如下。
(1)定义抽象的Colleague类:
java
public abstract class Colleague {
protected Mediator mediator;
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
public abstract boolean operation(String message); //同事类的操作
}
以上代码定义了抽象同事类Colleague,该类持有中介者对象并定义了同事类的具体操作方法operation()。
(2)定义Colleague实现类ColleagueLandlord以代表房东:
java
public class ColleagueLandlord extends Colleague {
private final static Log logger =
LogFactory.getLog(ColleagueLandlord.class);
@Override
public boolean operation(String message) {//收到房客的需求
logger.info("landlord receive a message form mediator:"+message);
return true;
}
}
以上代码定义了Colleague实现类ColleagueLandlord以代表房东,并实现了方法operation(),该方法用来接收中介者传递的房客需求并做出具体响应。
(3)定义Colleague实现类ColleagueTenant以代表租户:
java
public class ColleagueTenant extends Colleague {
private final static Log logger =LogFactory.getLog(ColleagueTenant.class);
@Override
public boolean operation(String message) {
logger.info("tenant receive a message form mediator:"+message);
return true;
}
}
以上代码定义了Colleague实现类ColleagueTenant以代表租户,并实现了方法operation(),该方法用来接收中介者传递的房东的房源信息并做出具体的响应。
(4)定义抽象中介者Mediator类:
java
public abstract class Mediator {
protected Colleague colleagueTenant;
protected Colleague colleagueLandlord;
public Mediator(Colleague colleagueTenant, Colleague
colleagueLandlord) {
this.colleagueTenant = colleagueTenant;
this.colleagueLandlord = colleagueLandlord;
}
public abstract boolean notifyColleagueTenant(String message);
public abstract boolean notifyColleagueLandlord(String message);
}
以上代码定义了抽象中介者Mediator类,该类持有租客和房东类的实例,并定义了notifyColleagueTenant()和notifyColleagueLandlord()分别向房客和房东传递信息。
(5)定义Mediator实现类ConcreteMediator代表一个具体的中介:
java
public class ConcreteMediator extends Mediator {
public ConcreteMediator(Colleague colleagueTenant, Colleague
colleagueLandlord) {
super(colleagueTenant, colleagueLandlord);
}
@Override
public boolean notifyColleagueTenant(String message) {
if (colleagueTenant ! = null) {
return colleagueTenant.operation(message);
}
return false;
}
@Override
public boolean notifyColleagueLandlord(String message) {
if (colleagueLandlord ! = null) {
return colleagueLandlord.operation(message);
}
return false;
}
}
以上代码定义了Mediator实现类ConcreteMediator来代表一个具体的中介,该中介实现了notifyColleagueTenant()和notifyColleagueLandlord()来完成房客和房东直接、具体的消息传递。
(6)使用中介者模式:
java
public static void main(String[] args) {
//定义房客同事类
Colleague colleagueTenant = new ColleagueTenant();
//定义房东同事类
Colleague colleagueLandlord = new ColleagueLandlord();
//创建一个具体的中间者,这里可以将其理解为房屋中介
ConcreteMediator concreteMediator = new
ConcreteMediator(colleagueTenant, colleagueLandlord);
boolean resoult = concreteMediator.notifyColleagueTenant(
"想租2室1厅的吗?");
if(resoult){
concreteMediator.notifyColleagueLandlord("租客对面积满意");
}else{
concreteMediator.notifyColleagueLandlord("租客对面积不满意");
}
}
在使用中介者模式时,首先要定义同事类,然后定义中介者并通过中介者完成对象之间的交互。以上代码首先定义了房客类和房东类,然后定义了中介者,最后通过中介者的notifyColleagueTenant()和notifyColleagueLandlord()完成房客和中间者之间的交互。以上代码的流程是中介者首先向房客询问对方对房屋面积的需求,然后将需求反馈给房东。具体的执行结果如下:
java
[INFO] ColleagueTenant - tenant receive a message form mediator:
想租2室1厅的吗?
[INFO] ColleagueLandlord - landlord receive a message form mediator:
租客对面积满意
24、解释器模式的概念及Java实现
解释器(Interpreter)模式给定一种语言,并定义该语言的语法表示,然后设计一个解析器来解释语言中的语法,这种模式常被用于SQL解析、符号处理引擎等。
解释器模式包含以下主要角色。
- 抽象表达式(Abstract Expression):定义解释器的接口,约定解释器所包含的操作,比如interpret()方法。
- 终结符表达式(Terminal Expression):抽象表达式的子类,用来定义语法中和终结符有关的操作,语法中的每一个终结符都应有一个与之对应的终结表达式。
- 非终结符表达式(Nonterminal Expression):抽象表达式的子类,用来定义语法中和非终结符有关的操作,语法中的每条规则都有一个非终结符表达式与之对应。
- 环境(Context):定义各个解释器需要的共享数据或者公共的功能。
解释器模式主要用于和语法及表达式有关的应用场景,例如正则表达式解释器等。具体的UML设计如图所示。

具体实现如下。
(1)定义Expression接口:
java
public interface Expression {
public void interpret(Context ctx); //解释方法
}
以上代码定义了Expression接口,并定义了解释器方法。
(2)定义NonterminalExpression类:
java
public class NonterminalExpression implements Expression {
private Expression left;
private Expression right;
public NonterminalExpression(Expression left, Expression right) {
this.left=left;
this.right=right;
}
public void interpret(Context ctx) {
//递归调用每一个组成部分的interpret()
//在递归调用时指定组成部分的连接方式,即非终结符的功能
}
}
以上代码定义了Expression的实现类NonterminalExpression, NonterminalExpression类主要用于对非终结元素的处理。NonterminalExpression定义了left和right的操作元素。
(3)定义TerminalExpression:
java
public class TerminalExpression implements Expression{
@Override
public void interpret(Context ctx) {
//终结符表达式的解释操作
}
}
以上代码定义了Expression的实现类TerminalExpression, TerminalExpression类主要用于对终结元素的处理,表示该元素是整个语法表达式的最后一个元素。
(4)定义Context:
java
public class Context {
private HashMap map = new HashMap();
public void assign(String key, String value) {
//在环境类中设值
}
public String get(String key) {
//获取存储在环境类中的值
return "";
}
}
以上代码定义了Context的全局类用于存储表达式解析出来的值,并提供查询和解析后表达式的结果,以便其他表达式进一步使用。