• 设计模式学习(五)——命令模式


    定义

    将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。支持可撤销的操作。

    特点

    1. 将发出请求的对象和执行请求的对象解耦。
    2. 通过command对象连接请求调用者与被调用者。
    3. 通过setCommand()方法改变调用者具体的执行体。
    4. 不同的command对象可以拥有不同的执行实体。
    5. 宏命令方式可以动态处理一系列的请求。
    6. 支持undo撤销。

    实际中的应用

    1. 事务:借助堆栈来记录操作过程,然后逐一回滚到checkpoint
    2. 日志系统:如数据库的binlog,如果每一个操作都要备份整个数据库这工作量太大了。不妨记录日志,还原时可以从上个备份点开始逐一执行日志记录的动作。
    3. 线程池:将命令放入队列,线程池中的固定数量线程去队列里消费。

    举例

    场景

    一个码农拥有一个音乐播放器和一台MacbookPro。它想一键执行或撤销如下等指令:

    • 在MacbookPro上打开网易云音乐客户端
    • 在MacbookPro上打开Chrome客户端
    • 调高音乐播放器的声音
    • 调低音乐播放器的声音
    • ……

    实现代码:

    (1) MacbookPro

    public class MacbookPro {
    	public void openChrome(){
    		System.out.println("open chrome.");
    	}
    	public void closeChrome(){
    		System.out.println("close chrome");
    	}
    	public void openNeteaseMusic(){
    		System.out.println("open netease music.");
    	}
    	public void closeNeteaseMusic(){
    		System.out.println("close netease music.");
    	}
    }
    

    (2) MusicPlayer

    public class MusicPlayer {
    	public static final String HIGH = "high";
    	public static final String MEDIUM = "medium";
    	public static final String LOW = "low";
    	private String voice;
    	
    	public MusicPlayer(){
    		voice = MEDIUM;
    	}
    	public String getVoice() {
    		return voice;
    	}
    	public void setVoice(String voice) {
    		this.voice = voice;
    	}
    	
    	public void up(){
    		String currentVoice = getVoice();
    		switch (currentVoice) {
    		case LOW:
    			setVoice(MEDIUM);
    			break;
    		case MEDIUM:
    		case HIGH:
    		default:
    			setVoice(HIGH);
    		}
    		System.out.println("Voice changed from " + currentVoice + " to " + getVoice());
    	}
    	
    	public void down(){
    		String currentVoice = getVoice();
    		switch (currentVoice) {
    		case HIGH:
    			setVoice(MEDIUM);
    			break;
    		case MEDIUM:
    		case LOW:
    		default:
    			setVoice(LOW);
    		}
    		
    		System.out.println("Voice changed from " + currentVoice + " to " + getVoice());
    	}
    }
    

    (3) Command接口

    public interface Command {
    	public void execute();
    	public void undo();
    }
    

    (4) OpenChromeCommand

    public class OpenChromeCommand implements Command{
    	MacbookPro macbookPro;
    	public OpenChromeCommand(MacbookPro macbookPro) {
    		this.macbookPro = macbookPro;
    	}
    
    	public void execute() {
    		macbookPro.openChrome();
    	}
    
    	public void undo() {
    		macbookPro.closeChrome();
    	}
    }
    

    (5) OpenNeteaseMusicCommand

    public class OpenNeteaseMusicCommand implements Command{
    	MacbookPro macbookPro;
    	public OpenNeteaseMusicCommand(MacbookPro macbookPro) {
    		this.macbookPro = macbookPro;
    	}
    
    	public void execute() {
    		macbookPro.openNeteaseMusic();
    	}
    
    	public void undo() {
    		macbookPro.closeNeteaseMusic();
    	}
    }
    

    (6) UpVoiceCommand

    public class UpVoiceCommand implements Command {
    	MusicPlayer musicPlayer;
    	String preVoice;
    	public UpVoiceCommand(MusicPlayer musicPlayer) {
    		this.musicPlayer = musicPlayer;
    		preVoice = musicPlayer.getVoice();
    	}
    	
    	public void execute() {
    		preVoice = musicPlayer.getVoice();
    		musicPlayer.up();
    	}
    
    	public void undo() {
    		musicPlayer.setVoice(preVoice);
    		System.out.println("Voice changed back to " + musicPlayer.getVoice());
    	}
    }
    

    (7) DownVoiceCommand

    public class DownVoiceCommand implements Command {
    	MusicPlayer musicPlayer;
    	String preVoice;
    	
    	public DownVoiceCommand(MusicPlayer musicPlayer) {
    		this.musicPlayer = musicPlayer;
    		preVoice = musicPlayer.getVoice();
    	}
    	
    	public void execute() {
    		preVoice = musicPlayer.getVoice();
    		musicPlayer.down();
    	}
    
    	public void undo() {
    		musicPlayer.setVoice(preVoice);
    		System.out.println("Voice changed back to " + musicPlayer.getVoice());
    	}
    }
    

    (8) Coder

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class Coder  {
    	List<Command> commands;
    	String coderName;
    	public Coder(String coderName){
    		commands = new ArrayList<>();
    		this.coderName = coderName;
    	}
    	
    	public String getCoderName() {
    		return coderName;
    	}
    
    	public void setCoderName(String coderName) {
    		this.coderName = coderName;
    	}
    
    	public void addCommand(Command command){
    		if (!commands.contains(command)){
    			this.commands.add(command);
    		}
    	}
    	
    	public void deleteCommamd(Command command){
    		this.commands.remove(command);
    	}
    	
    	public void setCommand(int index,  Command command){
    		if (commands.size()>index){
    			commands.set(index, command);
    		}
    	}
    	
    	public void replaceCommand(Command preCommand, Command curCommand){
    		int index=commands.indexOf(preCommand);
    		if ( index>=0 ){
    			setCommand(index, curCommand);
    		}
    	}
    	
    	public void clearCommands(){
    		commands.clear();
    	}
    	
    	public void execute(){
    		Iterator<Command> iterator = commands.iterator();
    		if (!iterator.hasNext()){
    			System.out.println("No command has been added.");
    			return;
    		}
    		
    		while (iterator.hasNext()){
    			Command command = iterator.next();
    			command.execute();
    		}
    	}
    	
    	public void undo(){
    		Iterator<Command> iterator = commands.iterator();
    		while (iterator.hasNext()){
    			Command command = iterator.next();
    			command.undo();
    		}
    	}
    	
    	public void print(){
    		StringBuffer stringBuffer = new StringBuffer();
    		Iterator<Command> iterator = commands.iterator();
    		while (iterator.hasNext()){
    			Command command = iterator.next();
    			stringBuffer.append(command.getClass().getSimpleName() + "-");
    		}
    		stringBuffer.deleteCharAt(stringBuffer.length()-1);
    		System.out.println(stringBuffer.toString());
    	}
    }
    

    (9) Main测试类

    public class Main {
    
    	public static void main(String[] args) {
    		Coder coder = new Coder("Nisir");
    		System.out.println(coder.getCoderName());
    		MacbookPro macbookPro = new MacbookPro();
    		MusicPlayer musicPlayer = new MusicPlayer();
    		Command  OpenChromeCommand = new OpenChromeCommand(macbookPro);
    		Command OpenNeteaseMusicCommand = new OpenNeteaseMusicCommand(macbookPro);
    		Command upVoiceCommand = new UpVoiceCommand(musicPlayer);
    		Command downVoiceCommand = new DownVoiceCommand(musicPlayer);
    		
    		System.out.println("=============Dividing Line1=============");
    		coder.addCommand(OpenChromeCommand);
    		coder.execute();
    		coder.undo();
    		System.out.println("=============Dividing Line2=============");
    		
    		coder.setCommand(0, OpenNeteaseMusicCommand);
    		coder.execute();
    		coder.undo();
    		System.out.println("=============Dividing Line3=============");
    		
    		coder.addCommand(OpenChromeCommand);
    		coder.execute();
    		coder.undo();
    		System.out.println("=============Dividing Line4=============");
    		
    		coder.clearCommands();
    		coder.execute();
    		coder.undo();
    		System.out.println("=============Dividing Line5=============");
    		
    		coder.addCommand(upVoiceCommand);
    		coder.execute();
    		
    		System.out.println("=============Dividing Line6=============");
    		
    		coder.replaceCommand(upVoiceCommand, downVoiceCommand);
    		coder.execute();
    		coder.undo();
    	}
    
    }
    

    (10) 测试结果:

    Nisir
    =============Dividing Line1=============
    open chrome.
    close chrome
    =============Dividing Line2=============
    open netease music.
    close netease music.
    =============Dividing Line3=============
    open netease music.
    open chrome.
    close netease music.
    close chrome
    =============Dividing Line4=============
    No command has been added.
    =============Dividing Line5=============
    Voice changed from medium to high
    =============Dividing Line6=============
    Voice changed from high to medium
    Voice changed back to high
    
    

    小结

    可以看到,coder程序员不需要知道它要调用的具体对象,它只要通过执行不同command对象,就可以去调用具体的执行实体。并且command对象还可以改变。此外,通过ArrayList还可以动态增减命令集合,以后如果有新的命令加入,可以动态扩展,一键执行!

    Azkaban中的命令模式

    (1) EventListener——Command

    package azkaban.event;
    
    public interface EventListener {
      public void handleEvent(Event event);
    }
    

    (2) EventHandler——Coder

    package azkaban.event;
    
    import java.util.ArrayList;
    import java.util.HashSet;
    
    public class EventHandler {
      private HashSet<EventListener> listeners = new HashSet<EventListener>();
    
      public EventHandler() {
      }
    
      public void addListener(EventListener listener) {
        listeners.add(listener);
      }
    
      public void fireEventListeners(Event event) {
        ArrayList<EventListener> listeners =
            new ArrayList<EventListener>(this.listeners);
        for (EventListener listener : listeners) {
          listener.handleEvent(event);
        }
      }
    
      public void removeListener(EventListener listener) {
        listeners.remove(listener);
      }
    }
    

    命令模式 vs 观察者模式

    个人感觉(可能并没有深刻体会到两者的精髓,希望有经验的人可以指点一二),两者非常相似,观察者模式中其实就嵌套使用了命令模式。

    在Subject主题调notifyObservers()方法通知各个观察者时,会遍历所有已注册的观察者对象Observer,调observer.update()方法来更新各个对象。

    观察者模式里,主题Subject类的registerObserver(Observer o)和removeObserver(Observer o)方法对应了命令模式里的add()和remove()。

    在观察者模式里,observer会有一个实例变量持有主题subject的引用,从而可以在observer类里调用subject.registerObserver()方法和subject.removeObserver()方法来向主题注册和注销自己。

  • 相关阅读:
    以中间件,路由,跨进程事件的姿势使用WebSocket
    傻瓜式解读koa中间件处理模块koa-compose
    企业管理系统前后端分离架构设计 系列一 权限模型篇
    vue权限路由实现方式总结
    3YAdmin-专注通用权限控制与表单的后台管理系统模板
    lazy-mock ,一个生成后端模拟数据的懒人工具
    vue-quasar-admin 一个包含通用权限控制的后台管理系统
    CSS中line-height与vertical-align
    IdentityServer4实现Token认证登录以及权限控制
    利用AOP实现SqlSugar自动事务
  • 原文地址:https://www.cnblogs.com/znicy/p/6753253.html
Copyright © 2020-2023  润新知