系统需要设计一个命令行界面,用户可输入命令来执行某项功能,系统的功能会不断增加,命令也会不断的增加
如何将一项一项的功能加入到这个命令行界面?
如何让命令行程序写好后,不因功能的添加而修改,又可以灵活的加入命令和功能?
下面以奶茶店售卖奶茶为例:
public class MashedTaroMilk { public void build() { System.out.println("奶茶师傅制作芋泥啵啵奶茶。。。"); } }
public class StrawBerryMilk { public void build() { System.out.println("奶茶师傅制作草莓奶茶。。。"); } }
服务员类:负责接待顾客售卖奶茶
public class Waiter { public void receiver(String command) { System.out.println("您选择了:" + command); switch (command) { case "芋泥啵啵奶茶" : new MashedTaroMilk().build(); break; case "草莓奶茶" : new StrawBerryMilk().build(); break; default : System.out.println("您点的奶茶本店没有。。。"); break; } } public void showMenu() { System.out.println("您好,本店有以下奶茶:"); System.out.println("\t芋泥啵啵奶茶"); System.out.println("\t草莓奶茶"); } }
测试类:
public class Client { public static void main(String[] args) { waiter(); } public static void waiter() { Waiter waiter = new Waiter(); waiter.showMenu(); Scanner scanner = new Scanner(System.in); System.out.println("请选择:"); String chooseMilk = scanner.nextLine(); waiter.receiver(chooseMilk); scanner.close(); } }
现在如果奶茶增加了呢,需要对服务员类中的switch语句进行修改,但是又不想修改,该怎么做呢
定义一个制作奶茶的接口,所有的奶茶实现它:
public interface Command { /** 制作奶茶接口 */ void build(); }
实现类:
public class AppleMilk implements Command { @Override public void build() { System.out.println("开始制作苹果奶茶。。。"); } }
public class PlainMilk implements Command { @Override public void build() { System.out.println("开始制作原味奶茶。。。"); } }
现在又招了一个女服务员,负责接待:
public class Waitress { private Map<String, Command> map = new HashMap<>(); public void addMilk(String name, Command command) { map.put(name, command); } public void receiver(String command) { System.out.println("您选择了:" + command); Command command1 = map.get(command); if (command1 != null) { command1.build(); } else { System.out.println("您点的奶茶本店没有。。。"); } } public void showMenu() { System.out.println("您好,本店有以下奶茶:"); map.keySet().forEach((item)->{ System.out.println("\t" + item); }); } }
测试类:
public class Client { public static void main(String[] args) { waitress(); } public static void waitress() { Waitress waitress = new Waitress(); waitress.addMilk("苹果奶茶", new AppleMilk()); waitress.addMilk("原味奶茶", new PlainMilk()); waitress.showMenu(); Scanner scanner = new Scanner(System.in); System.out.println("请选择:"); String chooseMilk = scanner.nextLine(); waitress.receiver(chooseMilk); scanner.close(); } }
上面改进之后,每增加一个新的奶茶,只需要实现接口即可
以命令的方式,解耦调用者和功能的具体实现者,降低了系统的耦合度,提高了灵活性
将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化
在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适
在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合
优点:
缺点:
使用命令模式可能会导致某些系统有过多的具体命令类
类图:
涉及的角色:
对应的类如下:
Client类:
public class Client { public static void main(String[] args) { Receiver receiver = new Receiver(); Command command = new ConcreteCommand(receiver); Invoker invoker = new Invoker(command); invoker.action(); } }
Command接口:
public interface Command { /** 执行方法 */ void execute(); }
ConcreteComman实现类:
public class ConcreteCommand implements Command { private Receiver receiver; public ConcreteCommand(Receiver receiver) { this.receiver = receiver; } @Override public void execute() { System.out.println("do something......"); receiver.action(); } }
Invoker类:
public class Invoker { private Command command; public Invoker(Command command) { this.command = command; } /** 行动方法 */ public void action() { command.execute(); } }
Receiver类:
public class Receiver { public Receiver() { } /** 行动方法 */ public void action() { } }
在美猴王大闹天宫之前,玉帝命令太白金星召美猴王上天,玉帝的这一道旨意就是一个命令,而太白金星则是负责将圣旨传到,具体的执行者则是美猴王:
抽象接口,所有的圣旨都需要实现它:
public interface ImperialEdictCommand { /** 抽象圣旨,具体圣旨都必须具备的接口 */ void command(); }
具体的命令,即召美猴王上天这一个具体的圣旨:
public class SkyReportsConcreteCommand implements ImperialEdictCommand { private MonkeyKingReceiver receiver; public SkyReportsConcreteCommand(MonkeyKingReceiver receiver) { this.receiver = receiver; } @Override public void command() { System.out.println("宣美猴王孙悟空上天报道!"); receiver.action(); } }
玉皇大帝,颁布圣旨:
public class TheJadeEmperorClient { public static void main(String[] args) { System.out.println("玉皇大帝颁布一道圣旨,宣美猴王上天报道!"); MonkeyKingReceiver receiver = new MonkeyKingReceiver(); ImperialEdictCommand command = new SkyReportsConcreteCommand(receiver); GreatWhitePlanetInvoker invoker = new GreatWhitePlanetInvoker(); invoker.setCommand(command); invoker.action(); } }
太白金星负责把旨意传达给美猴王:
public class GreatWhitePlanetInvoker { private ImperialEdictCommand command; public void setCommand(ImperialEdictCommand command) { this.command = command; } /** 请求者太白金星调用此方法,要求美猴王上天 */ public void action() { System.out.println("太白金星传玉帝圣旨!"); command.command(); } }
具体的执行者,就是美猴王:
public class MonkeyKingReceiver { public MonkeyKingReceiver() { } public void action() { System.out.println("美猴王孙悟空上天,大闹天宫!"); } }
类图:
命令模式的结构如下:
策略模式的结构如下:
策略模式:侧重的是一个行为的多个算法实现,可互换算法,比如优惠活动满减和打折都是算法,可以选择其中一个来买买买
命令模式:侧重的是为多个行为提供灵活的执行方式,比如上面的奶茶,每个顾客购买奶茶的命令都是一个行为,而不同的奶茶制作方式不一样,则就需要灵活的去制作奶茶