Java-第十八部分-设计模式-命令模式和访问者模式
设计模式全文
命令模式
- 案例,智能生活,有多个家电来自不同厂商,需要通过一个app来控制全部
- Command Pattern,向对象发送请求,不需要知道请求的接受者和具体操作,消除请求发送这与请求接受者之间的耦合
- 案例类图
- LightReceiver 实际上控制的类,命令执行者
public class LightReceiver {
void on() {
System.out.println("Light On");
}
void off() {
System.out.println("Light Off");
}
}
- Command,控制设备的缓冲命令层,接受命令,调用命令接受者去执行
public interface Command {
void excute();
void undo(); //撤销
}
- LightOffCommand,关命令,实际命令
public class LightOffCommand implements Command {
private LightReceiver lightReceiver;
public LightOffCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void excute() {
lightReceiver.off();
}
@Override
public void undo() {
lightReceiver.on();
}
}
- NoCommand,空命令,用于初始化每个按钮,调用空命令,什么都不做,这种设计模式省去了
空的判断
public class NoCommand implements Command{
@Override
public void excute() {
System.out.println("NoCommand");
}
@Override
public void undo() {
System.out.println("NoCommand");
}
}
- RemoteController,遥控器,控制层,相当于
Invoker
,命令调度者
public class RemoteController {
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];
Arrays.fill(onCommands, new NoCommand());
Arrays.fill(offCommands, new NoCommand());
undoCommand = new NoCommand();
}
//设置需要的命令
public void setCommand(int no, Command onCommand, Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
public void onButtonWasPushed(int no) {
//找到按下的开的按钮,并调用对应的方法
onCommands[no].excute();
//记录这次操作,用于撤销
undoCommand = onCommands[no];
}
public void offButtonWasPushed(int no) {
//找到按下的开的按钮,并调用对应的方法
offCommands[no].excute();
//记录这次操作,用于撤销
undoCommand = offCommands[no];
}
public void undoButtonWasPushed() {
undoCommand.undo();
undoCommand = new NoCommand();
}
}
JDBCTemplate源码
JdbcTemplate
中query
,相当于命令调用者
public void query(String sql, RowCallbackHandler rch) throws DataAccessException {
this.query((String)sql, (ResultSetExtractor)(new JdbcTemplate.RowCallbackHandlerResultSetExtractor(rch)));
}
StatementCallback
接口,类似于命令接口,有多个命令实现类
public interface StatementCallback<T> {
@Nullable
T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}
query
中的内部类QueryStatementCallback
实现了命令接口,作为具体的命令
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
QueryStatementCallback() {
}
@Nullable
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
Object var3;
try {
rs = stmt.executeQuery(sql);
var3 = rse.extractData(rs);
} finally {
JdbcUtils.closeResultSet(rs);
}
return var3;
}
public String getSql() {
return sql;
}
}
doInStatement
调用具体的执行者ExecuteStatementCallback
excute
调用,传入了ExecuteStatementCallback
,通过这个具体的命令调用方法,而其中的doInStatement
调用了具体执行,stmt
相当于命令执行者
小结
- 将发起请求和执行请求的对象解耦,发起请求的对象是调用者,通过命令层,调用接受者进行操作
- 容易设计一个命令队列,只需要把命令对象放入队列,实现多线程执行命令
- 可能导致某些系统有过多具体的内部类
- 空命令是一种设计模式,
省去了判空的操作
- 适用于,每一个按钮都是一条命令,模拟
CMD/DOS命令
,订单的撤销/恢复,触发-反馈机制
访问者模式
- 案例,测评系统,观众有多种,对歌手进行不同的评价
- Visitor Pattern,封装了一些作用于某种数据接口的各元素的的操作,在不改变数据结构的前提下,定义这些元素的新操作;将数据结构与数据操作分离,解决数据结构和操作之间的耦合性
- 原理,在
被访问的类
里面加入一个对外提供接待访问者
的接口 - 适用于,需要对一个对象结构中的对象,进行很多不同的操作,避免这些操作污染这些对象的类
- 双分派,得到执行的操作取决于
访问者和被访问者
的种类
增加状态类时,只需要增加
Visitor
的子类即可
- 元素固定,不想对元素做频繁修改,在不改变代码的情况下,为元素绑定一个行为
- 将行为和元素解耦,利于维护,不改变代码的情况下,对元素和行为进行扩展
- Action,访问者,一种访问行为
public abstract class Action {
public abstract void getManResult(Man man);
public abstract void getWomanResult(Woman woman);
}
- Success,Action的实现,根据
被访问者的不同
去调用
public class Success extends Action {
@Override
public void getManResult(Man man) {
System.out.println("Man success");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println("Woman success");
}
}
- Person,被访问者,元素的抽象
public abstract class Person {
public abstract void accept(Action action);
}
- Man,元素的实体,接受一个访问者,并通过
双分派
,将自己传给访问者,通过访问者调用方法
- 第一次,在客户端中,将具体的状态作为参数传给了元素
- 第二次,元素调用了作为
参数的状态具体方法
,同时将自己this
作为参数传入- 完成元素和行为的双向绑定
public class Man extends Person {
@Override
public void accept(Action action) {
action.getManResult(this);
}
}
- ObjectStructure,管理所有元素
public class ObjectStructure {
private List<Person> personList = new LinkedList<>();
public List<Person> getPersonList() {
return personList;
}
public void setPersonList(List<Person> personList) {
this.personList = personList;
}
//增加
public void attach(Person p) {
personList.add(p);
}
//移除
public void remove(Person p) {
personList.remove(p);
}
public void display(Action action) {
for (Person p : personList) {
p.accept(action);
}
}
}
小结
- 符合单一职责原则,具有优秀的扩展性,对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
- 具体元素对
访问者
需要公布细节,访问者关注了其他类的内部细节
,增加了行为和元素的了解上的耦合
,也就是这个行为很可能只适用于这类元素
,这是迪米特法则不建议的 - 违背了依赖倒转原则,访问者依赖的是具体的元素,而不是抽象的元素
可以改,将行为绑定上抽象的元素,在方法内进行判断
- 适用于,数据结构比较稳定,但是行为功能比较多的需求
转载自:https://juejin.cn/post/7057084309533884430