likes
comments
collection
share

Java-第十八部分-设计模式-命令模式和访问者模式

作者站长头像
站长
· 阅读数 16

设计模式全文

命令模式

  • 案例,智能生活,有多个家电来自不同厂商,需要通过一个app来控制全部
  • Command Pattern,向对象发送请求,不需要知道请求的接受者和具体操作,消除请求发送这与请求接受者之间的耦合 Java-第十八部分-设计模式-命令模式和访问者模式
  • 案例类图 Java-第十八部分-设计模式-命令模式和访问者模式
  • 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源码

Java-第十八部分-设计模式-命令模式和访问者模式

  • JdbcTemplatequery,相当于命令调用者
public void query(String sql, RowCallbackHandler rch) throws DataAccessException {
    this.query((String)sql, (ResultSetExtractor)(new JdbcTemplate.RowCallbackHandlerResultSetExtractor(rch)));
}

Java-第十八部分-设计模式-命令模式和访问者模式

  • StatementCallback接口,类似于命令接口,有多个命令实现类 Java-第十八部分-设计模式-命令模式和访问者模式
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 Java-第十八部分-设计模式-命令模式和访问者模式
  • excute调用,传入了ExecuteStatementCallback,通过这个具体的命令调用方法,而其中的doInStatement调用了具体执行,stmt相当于命令执行者 Java-第十八部分-设计模式-命令模式和访问者模式 Java-第十八部分-设计模式-命令模式和访问者模式

小结

  • 将发起请求和执行请求的对象解耦,发起请求的对象是调用者,通过命令层,调用接受者进行操作
  • 容易设计一个命令队列,只需要把命令对象放入队列,实现多线程执行命令
  • 可能导致某些系统有过多具体的内部类
  • 空命令是一种设计模式,省去了判空的操作
  • 适用于,每一个按钮都是一条命令,模拟CMD/DOS命令,订单的撤销/恢复,触发-反馈机制

访问者模式

  • 案例,测评系统,观众有多种,对歌手进行不同的评价
  • Visitor Pattern,封装了一些作用于某种数据接口的各元素的的操作,在不改变数据结构的前提下,定义这些元素的新操作;将数据结构与数据操作分离,解决数据结构和操作之间的耦合性
  • 原理,在被访问的类里面加入一个对外提供接待访问者的接口
  • 适用于,需要对一个对象结构中的对象,进行很多不同的操作,避免这些操作污染这些对象的类 Java-第十八部分-设计模式-命令模式和访问者模式
  • 双分派,得到执行的操作取决于访问者和被访问者的种类

增加状态类时,只需要增加Visitor的子类即可 Java-第十八部分-设计模式-命令模式和访问者模式

  1. 元素固定,不想对元素做频繁修改,在不改变代码的情况下,为元素绑定一个行为
  2. 将行为和元素解耦,利于维护,不改变代码的情况下,对元素和行为进行扩展
  • 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,元素的实体,接受一个访问者,并通过双分派,将自己传给访问者,通过访问者调用方法
  1. 第一次,在客户端中,将具体的状态作为参数传给了元素
  2. 第二次,元素调用了作为参数的状态具体方法,同时将自己this作为参数传入
  3. 完成元素和行为的双向绑定
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、拦截器与过滤器,适用于数据结构相对稳定的系统
  • 具体元素对访问者需要公布细节,访问者关注了其他类的内部细节,增加了行为和元素的了解上的耦合,也就是这个行为很可能只适用于这类元素,这是迪米特法则不建议的
  • 违背了依赖倒转原则,访问者依赖的是具体的元素,而不是抽象的元素

可以改,将行为绑定上抽象的元素,在方法内进行判断

  • 适用于,数据结构比较稳定,但是行为功能比较多的需求