(4).使用场景
优点:
策略类之间可以自由切换
: 由于策略类都是先同一个接口,所以可以自由的切换。易于扩展
: 增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合开闭原则避免使用多重条件选择语句(if else)
,充分体现了面向对象的思想
缺点:
客户端必须知道所有的策略类
,并且自行决定使用哪一个策略- 策略模式将造成产生很多策略类,可以通过享元模式在一定程度上减少对象的创建。
使用场景:
- 一个系统需要动态的在
几种算法中选择一种
时,可将算法封装到策略类中 - 一个类定义了多种行为,并且这些行为在这个类中的操作以多个条件语句的形式出现,
可将每个条件分支一如他们各自的策略类中以代替这些条件语句
。 - 系统中各个
算法彼此完全独立
,且要求对用户隐藏具体算法的实现细节时。 - 系统要求使用算法的客户不应该知道其操作的数据时,可使用
策略模式来隐藏与算法相关的数据结构
。 - 多个类只区别在表现型为不同,可以使用策略模式,在运行时动态选择具体执行的行为。
(5).JDK源码解析
Comparator
中的策略模式在Arrays
类中有一个Sort()
方法。
1. sort()方法时排序的方法 2. 每一个排序的方法后面都对应着不同的算法,升序和降序算法。
3.命令模式
(1).命令模式
日常生活中,我们出去吃饭会遇到如下的情况。
顾客(点餐)->女服务员(接受订单)->女服务员转给厨师(厨师佐餐)
定义:
将一个请求封装为对象,使发出请求的责任(服务员)
和执行请求的责任(厨师)
分隔开。这样两者之间通过命令对象进行沟通,这样方便命令对象进行存储、传递、调用、增加与管理。
(2).结构
命令模式包含以下主要角色:
- 抽象命令角色: 定义命令的
接口
,声明执行的方法。 - 具体命令角色: 具体的命令,
实现命令接口
;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。 - 实现者/接收者角色: 接收者,
真正执行命令的对象(厨师)
。任何类都可以成为一个接收者,只要它能够实现命令要求实现的响应功能。 - 调用者/请求者角色:
要求命令对象执行请求(服务员)
,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行响应的操作的地方,也就是说相当于使用命令对象的入口。
(3).案列实现
将上面的案列用代码实现,那我们就需要分析命令模式的角色在该案列中由谁来充当。
服务员
: 调用者角色。
资深大厨
: 结接收者角色。
订单
: 命令中包含订单。
订单
package com.jsxs.behavioralModel.command; import java.util.HashMap; import java.util.Map; /** * @Author Jsxs * @Date 2023/4/23 13:08 * @PackageName:com.jsxs.behavioralModel.command * @ClassName: Order * @Description: TODO 订单类 * @Version 1.0 */ public class Order { // 餐桌号码 private int diningTable; // 所属的餐品及分数 private Map<String,Integer> foodDir=new HashMap<String,Integer>(); public int getDiningTable() { return diningTable; } public void setDiningTable(int diningTable) { this.diningTable = diningTable; } public Map<String, Integer> getFoodDir() { return foodDir; } // 餐品的名称和数量 public void setFood(String name,int num) { foodDir.put(name,num); } }
接收者
package com.jsxs.behavioralModel.command; /** * @Author Jsxs * @Date 2023/4/23 13:15 * @PackageName:com.jsxs.behavioralModel.command * @ClassName: SeniorChef * @Description: TODO 厨师 * @Version 1.0 */ public class SeniorChef { // 做餐 public void makeFood(String name,int num){ System.out.println(num+" 份 "+name); } }
抽象命令
package com.jsxs.behavioralModel.command; /** * @Author Jsxs * @Date 2023/4/23 13:16 * @PackageName:com.jsxs.behavioralModel.command * @ClassName: Command * @Description: TODO 抽象命令类 * @Version 1.0 */ public interface Command { void execute(); //执行命令 }
具体命令
package com.jsxs.behavioralModel.command; import java.util.Map; import java.util.Set; /** * @Author Jsxs * @Date 2023/4/23 13:17 * @PackageName:com.jsxs.behavioralModel.command * @ClassName: OrderCommand * @Description: TODO * @Version 1.0 */ public class OrderCommand implements Command{ // 持有接收者对象 private SeniorChef seniorChef; // 持有订单对象 private Order order; // 有参构造方法进行赋值的操作 public OrderCommand(SeniorChef seniorChef, Order order) { this.seniorChef = seniorChef; this.order = order; } // @Override public void execute() { System.out.println(order.getDiningTable()+"号桌的订单是:"); // 这里存放的是我们要做的全部餐品 Map<String, Integer> foodDir = order.getFoodDir(); // 遍历map集合 Set<String> strings = foodDir.keySet(); //获取所有键的集合 // 做餐 for (String foodName : strings) { seniorChef.makeFood(foodName,foodDir.get(foodName)); // 餐名和数量 } System.out.println(order.getDiningTable()+"号桌子的饭做好了"); } }
请求者
package com.jsxs.behavioralModel.command; import java.util.ArrayList; import java.util.List; /** * @Author Jsxs * @Date 2023/4/23 13:27 * @PackageName:com.jsxs.behavioralModel.command * @ClassName: Waitor * @Description: TODO 服务员 (请求者角色) * @Version 1.0 */ public class Waitor { // 持有多个命令对象 private List<Command> commands = new ArrayList<>(); // 将单个命令存放到集合中 public void setCommand(Command command) { commands.add(command); } // 发起命令的功能 public void orderUp(){ System.out.println("美女服务员说: 来订单了..."); // 因为命令在集合中 for (Command command : commands) { if (command!=null){ command.execute(); // 执行 } } } }
测试类
package com.jsxs.behavioralModel.command; /** * @Author Jsxs * @Date 2023/4/23 13:32 * @PackageName:com.jsxs.behavioralModel.command * @ClassName: Client * @Description: TODO * @Version 1.0 */ public class Client { public static void main(String[] args) { // 订单对象 Order order1 = new Order(); order1.setDiningTable(1); order1.setFood("西红柿鸡蛋面",1); order1.setFood("可乐",2); Order order2 = new Order(); order2.setDiningTable(2); order2.setFood("炒饭",1); order2.setFood("雪碧",2); // 创建厨师对象 SeniorChef seniorChef = new SeniorChef(); // 创建命令对象 OrderCommand orderCommand = new OrderCommand(seniorChef, order1); OrderCommand orderCommand2 = new OrderCommand(seniorChef, order2); //创建调用者也就是服务员 Waitor waitor = new Waitor(); waitor.setCommand(orderCommand); waitor.setCommand(orderCommand2); waitor.orderUp(); } }
(4).使用场景
优点:
- 降低了系统的耦合度,命令模式能将调用操作的对象与实现该操作的对象解耦。
- 增加或删除命令非常方便。采用命令模式增加或删除命令不会影响其他类,它满足"开闭原",对扩展比较灵活。
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
- 方便实现 Undo(命令撤销) 和 Redo(命令恢复) 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类。
- 系统结构更加复杂。
使用场景:
- 系统需要将请求调用者和请求接受者解耦,使得调用者和接收者部直接交互。
- 系统需要在不同的事件指定请求、将
请求排队(List)
和执行请求。 - 系统需要支持命令的撤销操作(Undo)和恢复操作(Redo).
(5).JDK源码解析
Runable
是一个典型的命令模式,Runnable
担当命令的角色,Thread(服务员)
充当的是调用者,start
方法就是执行方法。