示例

系统需要设计一个命令行界面,用户可输入命令来执行某项功能,系统的功能会不断增加,命令也会不断的增加

如何将一项一项的功能加入到这个命令行界面?

如何让命令行程序写好后,不因功能的添加而修改,又可以灵活的加入命令和功能?

下面以奶茶店售卖奶茶为例:

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();
}
}



上面改进之后,每增加一个新的奶茶,只需要实现接口即可

命令模式

定义

以命令的方式,解耦调用者和功能的具体实现者,降低了系统的耦合度,提高了灵活性

意图

将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化

主要解决问题

在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适

何时使用

在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合

优缺点

优点:

  1. 降低了系统的耦合度
  2. 提高了灵活性

缺点:

使用命令模式可能会导致某些系统有过多的具体命令类

类图:



涉及的角色:

  1. 客户(Client)角色:创建了一个具体命令(ConcreteCommand)对象并确定其接收者
  2. 命令(Command)角色:声明了一个给所有具体命令类的抽象接口,通常由接口或者抽象类实现
  3. 具体命令(ConcreteCommand)角色:定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作,execute()方法通常叫做执行方法
  4. 请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法
  5. 接收者(Receiver)角色:负责具体实施和执行一个请求,任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法

对应的类如下:

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("美猴王孙悟空上天,大闹天宫!");
}
}



类图:

命令模式和策略模式的区别

命令模式的结构如下:



策略模式的结构如下:



策略模式:侧重的是一个行为的多个算法实现,可互换算法,比如优惠活动满减和打折都是算法,可以选择其中一个来买买买

命令模式:侧重的是为多个行为提供灵活的执行方式,比如上面的奶茶,每个顾客购买奶茶的命令都是一个行为,而不同的奶茶制作方式不一样,则就需要灵活的去制作奶茶

玉帝传美猴王上天,大闹天宫之Java设计模式:命令模式的更多相关文章

  1. 【设计模式】Java设计模式 - 命令模式

    Java设计模式 - 命令模式 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目录 Ja ...

  2. Java设计模式-命令模式(Command)

    命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行.这个过程好在,三者相互解耦,任何一方都不用去依赖其 ...

  3. JAVA 设计模式 命令模式

    用途 命令模式 (Command) 将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化:对请求排队或请求日志,以及支持可撤销的操作. 命令模式是一种行为型模式. 结构

  4. JAVA设计模式---命令模式

    1.定义: 将“请求”封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象,命令模式也支持可撤销的操作.命令可以用来实现日志和事务系统. 2.实例: 1)需求:设计一个家电遥控器的API,遥控 ...

  5. Java设计模式—命令模式

    命令模式是一个高内聚的模式. 定义如下:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 通用类图如下: 角色说明: ● Re ...

  6. Java设计模式の命令模式

    意图: 将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化:对请求排队或记录日志,以及支持可撤销的操作 动机: 将”发出请求的对象”和”接收与执行这些请求的对象”分隔开来. 效果: 1).c ...

  7. Java设计模式-代理模式之动态代理(附源代码分析)

    Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象. 上篇中的静态代 ...

  8. 【设计模式】Java设计模式 - 桥接模式

    [设计模式]Java设计模式 - 桥接模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起 ...

  9. Java设计模式——组合模式

    JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...

随机推荐

  1. winform导出excel

    public void AllDataSetToExcel(DataSet ds) { string saveFileName = ""; bool fileSaved = fal ...

  2. SpringCloud之服务配置

    1.config 1.1定义 对于分布式微服务,有很多的配置,那么修改起来很麻烦.这就需要对这些配置文件进行集中式的管理,config的功能就是用来统一管理配置文件的.它为微服务提供集中化的外部配置支 ...

  3. Maven 打包项目到私服 (deploy)

    一.配置maven 在maven安装目录 /conf/setting.xml 中的servers下添加: 1 <servers> 2 <server> 3 <id> ...

  4. 由剑指offer引发的思考——对象中虚函数指针的大小

    先看一个简单的问题: 一.定义一个空的类型,对于其对象我们sizeof其大小,是1字节.因为我们定义一个类型,编译器必须为其分配空间,具体分配多少是编译器决定,vs是1字节,分配在栈区. 那,这一个字 ...

  5. CRLF注入原理

    CRLF 指的是回车符(CR,ASCII 13,\r,%0d) 和换行符(LF,ASCII 10,\n,%0a),操作系统就是根据这个标识来进行换行的,你在键盘输入回车键就是输出这个字符,只不过win ...

  6. 《C++ Primer》笔记 第9章 顺序容器

    顺序容器类型 类型 解释 vector 可变大小数组.支持快速随机访问.在尾部之外的位置插入或删除元素可能很慢 deque 双端队列.支持快速随机访问.在头尾位置插入.删除速度很快 list 双向链表 ...

  7. CCF(消息传递口:80分):字符串相关+队列

    消息传递口 201903-4 本题主要是利用队列进行模拟,因为一开始我没有注意到要按照顺序,所以一开始的解法错误. #include<iostream> #include<algor ...

  8. CSDN博客转MD格式

    基于大神作品修改原文,使用了一下发现有一些小问题,爬取的博客标题如果含有字符是Windows不支持的命名格式,会卡在界面,进行了一下优化,加了一些字符过滤处理,但是tomd模块对html的处理还是不是 ...

  9. Java 基础加强 02

    基础加强·反射 和 枚举 类的加载概述和加载时机 * A:类的加载概述 * 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载.连接.初始化来实现对这个类的初始化 * 加载 * 就是指 ...

  10. C#的常见集合接口提供的功能

    C#的常见集合接口提供的功能 这里的功能都是泛型版本的常见功能,列出来,也许后面用得上吧,没有放非泛型版本,因为觉得用得不多,也就没有整理 IEnumerable<T> ICollecti ...