【设计模式】命令模式(Command)

概述

  • 命令模式又称为行动(Action) 模式或交易(Transaction) 模式。
  • 命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化;对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能
  • 命令模式是对命令的封装。命令模式把命令的责任执行命令的责任分割开,委派给不同的对象。

模式结构

【设计模式】命令模式(Command)_第1张图片

示意性代码

namespace 命令模式
{
    class Receiver
    {
        public void Action()
        {
            Console.WriteLine("操作成功!");
        }
    }
    abstract class Command
    {
        protected Receiver receiver;

        public Command(Receiver receiver)
        {
            this.receiver = receiver;
        }

        abstract public void Execute();
    }
    
    class ConcreteCommand:Command
    {
        public ConcreteCommand(Receiver receiver) : base(receiver) { }

        public override void Execute()
        {
            receiver.Action();
        }
    }

    class Invoker
    {
        private Command command;

        public void SetCommand(Command command)
        {
            this.command = command;
        }

        public void ExecuteCommand()
        {
            command.Execute();
        }    
    }
    class Program
    {
        static void Main(string[] args)
        {
            Receiver r = new Receiver();
            Command c = new ConcreteCommand(r);
            Invoker i = new Invoker();
            i.SetCommand(c);
            i.ExecuteCommand();
            Console.Read();
        }
    }
}
  • 将请求的接收者(Receiver)放到Command的具体子类ConcreteCommand中,当请求到来时(Invoker发出Invoker消息激活Command对象),ConcreteCommand将处理请求交给Receiver对象进行处理
  • Command模式将调用操作的对象和知道如何实现该操作的对象解耦。在上面Command的结构图中,Invoker对象根本就不知道具体的是哪个对象在处理Excute操作。
  • 在Command下要增加新的处理操作对象很容易,我们可以通过创建新的继承自Command的子类来实现这一点。
  • Command模式可以和Memento模式结合起来,支持Undo的操作。

使用命令模式情况

  • 如果需要在不同的时刻指定、排列和执行请求,可以选用命令模式,把这些请求封装成为命令对象,然后实现把请求队列化
  • 如果需要支持取消操作从可以选用命令模式,通过管理命令对象,,能很容易的实现命令的恢复和重做的功能
  • 如果需要支持当系统崩溃时,能把对系统的操作功能重新执行一遍,可以选用命令模式,把这些操作功能的请求封装成命令对象,然后实现日志命令,就可以在系统恢复回来后,通过日志获取命令列表,从而重新执行一遍功能
  • 在需要事务的系统中,可以选用命令模式,命令模式提供了对事务进行建模的方法,命令模式有二个别名就是Transaction.

优点

命令允许请求的一方和接收请求的一方能够独立演化。

  • 命令模式使新的命令很容易地被加入到系统里
  • 允许接收请求的一方决定是否要否决(Veto) 请求。
  • 能较容易地设计一个命令队列。
  • 可以容易地实现对请求的Undo和Redo。
  • 在需要的情况下,可以较容易地将命令记入日志。
  • 命令模式把请求一个 操作的对象与知道怎么执行一个操作的对象分割开。
  • 命令类与其他任何别的类-样,可以修改和推广。

本质

封装请求。

实例

ps:代码仅代表个人思路
【问题】使用命令模式设计一个简易计算器
【代码】

public abstract class Command {
	abstract public void execute();
	abstract public void unExcute();
}
public class CalculatorCommand extends Command {
	private char operator;
	private int operand;
	private Calculator calculator;
	
	public CalculatorCommand() {}

	public CalculatorCommand(char operator, int operand, Calculator calculator) {
		super();
		this.operator = operator;
		this.operand = operand;
		this.calculator = calculator;
	}

	public void setOperator(char operator) {
		this.operator = operator;
	}

	public void setOperand(int operand) {
		this.operand = operand;
	}

	public void setCalculator(Calculator calculator) {
		this.calculator = calculator;
	}

	@Override
	public void execute() {
		calculator.operation(operator,operand);
	}

	@Override
	public void unExcute() {
		if(calculator.getNum()!=0) {
			calculator.operation(undo(operator), operand);
			calculator.setNum(calculator.getNum()-2);
		}
	}
	
	private char undo(char operator) {
		switch (operator) {
		case '+':
			operator='-';
			break;
		case '-':
			operator='+';
			break;
		case '*':
			operator='/';
			break;
		case '/':
			operator='*';
			break;
		}
		return operator;
	}
}
public class Calculator {
	private int total = 0;
	private int num = 0;

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}
	
	public void operation(char operator, int operand) {
		if (num == 0) {
			num++;
			total = operand;
		} else {
			switch (operator) {
			case '+':
				total += operand;
				num++;
				break;
			case '-':
				total -= operand;
				num++;
				break;
			case '*':
				total *= operand;
				num++;
				break;
			case '/':
				total /= operand;
				num++;
				break;
			}
		}
		System.out.println("Total="+total);
	}
}
public class User {
	private Calculator calculator=new Calculator();
	private Command[] commands=new Command[10000000];
	private int current=0;
	
	public void redo(int levels) {
		for(int i=0;i<levels;i++) {
			if(current<commands.length-1) {
				commands[current++].execute();
			}
		}
	}
	public void undo(int levels) {
		for(int i=0;i<levels;i++) {
			if(current>0) {
				commands[--current].unExcute();
			}
		}
	}
	public void compute(char operator ,int operand) {
		Command command=new CalculatorCommand(operator,operand,calculator);
		command.execute();
		commands[current++]=command;
	}
}
public class Client {

	public static void main(String[] args) {
		User user =new User();
		
		user.compute('+', 100);
		user.compute('-', 50);
		user.compute('*', 10);
		user.compute('/', 2);
		
		user.undo(4);
		user.redo(3);
	}
}

【UML图】
【设计模式】命令模式(Command)_第2张图片

你可能感兴趣的:(设计模式)