模板方法模式
定义了一个操作中的算法骨架,将算法的一些步骤推迟到子类,使得子类可以不改变该算法结构的情况下重定义该算法的某些步骤
【主要角色
】:
- 抽象类:给出一个算法的轮廓和骨架(包括一个模板方法 和 若干基本方法)
- 模板方法:定义了算法的骨架(执行顺序),按某种顺序调用其包含的基本方法
- 基本方法:实现算法各个步骤的方法
- 抽象方法:由抽象类声明,具体子类实现
- 具体方法:由抽象类或具体类声明或实现,子类可以覆盖也可以继承
- 钩子方法:在抽象类中已经实现,包含用于判断的逻辑方法和需要子类重写的空方法(一般是用于判断的逻辑方法,方法名为isXxx,返回值为boolean类型)
- 具体子类:实现抽象类中所定义的抽象方法和钩子方法
案例:炒菜
【需求
】:炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。
抽象类(定义模板方法和基本方法):
public abstract class AbstractClass {
// 模板方法定义(子类不能修改模板方法)
public final void cookProcess() {
pourOil();
heatOil();
pourVegetable();
pourSauce();
fry();
}
public void pourOil() {
System.out.println("倒油");
}
public void heatOil() {
System.out.println("热油");
}
// 倒蔬菜(一个是包菜、一个是菜心)
public abstract void pourVegetable();
// 倒调味品
public abstract void pourSauce();
public void fry() {
System.out.println("翻炒");
}
}
炒包菜类、炒菜心类(具体子类):
public class ConcreteClassBaoCai extends AbstractClass {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是包菜");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是辣椒");
}
}
public class ConcreteClassCaiXin extends AbstractClass {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是菜心");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是蒜蓉");
}
}
测试类:
public class Client {
public static void main(String[] args) {
// 创建对象
AbstractClass baoCai = new ConcreteClassBaoCai();
// 调用炒菜功能
baoCai.cookProcess();
}
}
将相同的代码放在抽象的父类中,把不同的代码放入不同的子类中。
适用场景
- 算法的整体部分比较固定,个别部分容易改变,就可以使用模板方法,将容易变化的部分抽象出来,让子类去实现这些方法。
- 通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。
策略模式
该模式定义了一系列算法, 使他们可以互相替换,算法的变化不会影响使用算法的客户。
【主要角色
】:
- 抽象策略类:由一个接口或抽象类实现,给出所有具体策略所需的接口
- 具体策略类:实现了抽象策略类定义的接口,提供具体的算法实现或行为
- 环境类:持有一个策略类的引用,最终给客户端调用
案例:促销活动
【需求
】:超市争对不同的节日(春节、中秋节、圣诞节)推出不同的促销活动,由促销员(环境类)将促销活动展示给客户。
抽象策略类:
public interface Strategy {
void show();
}
策略A、B、C类(具体策略类):
public class StrategyA implements Strategy {
@Override
public void show() {
System.out.println("买一送一");
}
}
public class StrategyB implements Strategy {
@Override
public void show() {
System.out.println("满200减50");
}
}
public class StrategyC implements Strategy {
@Override
public void show() {
System.out.println("打8折");
}
}
销售员类(环境类):
public class SalesMan {
private Strategy strategy;
public SalesMan(Strategy strategy) {
this.strategy = strategy;
}
// 由促销员展示促销活动给普通用户
public void salesManShow() {
strategy.show();
}
}
测试类:
public class Client {
public static void main(String[] args) {
// 切换策略A
SalesMan salesMan = new SalesMan(new StrategyA());
salesMan.salesManShow();
// 切换策略B
salesMan = new SalesMan(new StrategyB());
salesMan.salesManShow();
// 切换策略C
salesMan = new SalesMan(new StrategyC());
salesMan.salesManShow();
}
}
策略类之间可以自由切换;
增加一个新的策略只需要添加一个具体的策略类
策略模式可以避免使用多重的if else 和 switch case
适用场景
- 需要动态在几种算法中选择一种,可以把每个算法封装到策略类中
- 大量的if else和switch cash完全可以用策略模式代替,让代码更加美观优雅
命令模式
将请求封装成一个对象,使发出请求的责任和执行请求的责任分隔开,这样两者之间通过命令对象进行沟通,方便将命令对象进行存储、传递、调用、增加、管理。
【主要角色
】
- 抽象命令角色:定义命令的接口,声明执行方法
- 具体命令角色:实现命令接口,通常会持有接收者,并调用接收者的功能来完成命令要执行的操作
- 实现者 / 接收者角色:真正执行命令的对象
- 调用者 / 请求者角色:要求命令对象执行请求,通常会持有命令对象
案例:点餐
- 服务员:调用者角色,由服务员发起命令
- 厨师:接收者命令,真正命令执行的对象
- 订单:命令中包含订单
订单类:
@Data
public class Order {
// 餐桌号码
private int diningTable;
// 所下餐品和份数
private Map<String, Integer> foodDir = new HashMap<>();
// 添加食物
public void addFood(String name, int num) {
foodDir.put(name, num);
}
}
厨师类(接收者):
public class Chef {
// 制作食物
public void makeFood(String name, int num) {
System.out.println(num + "份" + name);
}
}
抽象命令类:
public interface Command {
void execute();
}
具体命令类:
public class OrderCommand implements Command {
// 持有接收者对象
private Chef receiver;
private Order order;
public OrderCommand(Chef receiver, Order order) {
this.receiver = receiver;
this.order = order;
}
@Override
public void execute() {
System.out.println(order.getDiningTable() + "桌的订单:");
Map<String, Integer> foodDir = order.getFoodDir();
foodDir.forEach((foodName, num)->{
receiver.makeFood(foodName, num); // 让厨师去完成订单里的菜品
});
System.out.println(order.getDiningTable() + "桌的饭准备完毕");
}
}
服务员类(调用者角色):
public class Waitor {
// 持有多个命令对象
private List<Command> commands = new ArrayList<>();
public void addCommand(Command command) {
// 将command存储到List集合中
commands.add(command);
}
// 发起命令
public void orderUp() {
System.out.println("服务员:厨师,订单来啦");
commands.forEach(command -> {
if(command != null) command.execute();
});
}
}
测试类:
public class Client {
public static void main(String[] args) {
// 创建第一个订单对象
Order order1 = new Order();
order1.setDiningTable(1);
order1.addFood("西红柿鸡蛋面", 1);
order1.addFood("小杯可乐", 2);
// 创建第二个订单对象
Order order2 = new Order();
order2.setDiningTable(2);
order2.addFood("油闷大虾", 1);
order2.addFood("小杯雪碧", 1);
// 创建厨师对象
Chef receiver = new Chef();
// 创建命令对象
OrderCommand cmd1 = new OrderCommand(receiver, order1);
OrderCommand cmd2 = new OrderCommand(receiver, order2);
// 创建调用者
Waitor invoke = new Waitor();
invoke.addCommand(cmd1);
invoke.addCommand(cmd2);
// 让服务员发起命令
invoke.orderUp();
}
}
适用场景
- 系统需要将请求调用这和请求接收者解耦
- 系统需要在不同时间指定请求、将请求排队和执行请求
- 系统需要支持命令的撤销(undo)和恢复(redo)操作
JDK源码解析:Runnable类
Runnable类就是一个命令模式,Thread是调用者,start()方法就是执行方法
责任链模式
避免发送者和多个请求处理者耦合在一起,将所有请求的处理者通过钱一个对象记住其下一个对象的引用而连成一条链;当请求发生时,可以将请求沿着这条链传递,直到有对象处理它为止
【
问题
】公司员工请假,可以批假的领导有:部门负责人、副总经理、总经理,但是每个领导可以批准的天数不同。员工需要根据自己要请假的天数去找不同的领导签字。
【解决
】:比如有个员工要请假,他只需要去找部门负责人,如果部门负责人发现他请假的时间自己处理不了,就会把这个请假单交给副总经理,如果副总经理处理不了,就会交给总经理处理。
【主要角色
】:
- 抽象处理者角色:定义一个处理请求的接口,包含抽象处理方法和后继处理方法
- 具体处理者角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理就处理;如果不能处理就把它转发给后继的处理者
- 客户类角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程
案例:请假流程
【需求
】:请假一天以下的只需要小组长同意;请假1-3天需要部门经理同意;请假3-7天需要总经理同意。
请假条类:
@AllArgsConstructor
@Data
public class LeaveRequest {
// 姓名
private String name;
// 请假天数
private int num;
// 请假内容
private String content;
}
抽象处理者类:
@Data
public abstract class Handler {
protected final static int NUM_ONE = 1;
protected final static int NUM_THREE = 3;
protected final static int NUM_SEVEN = 7;
// 领导了可以处理的天数区间
private int numStart;
private int numEnd;
public Handler(int numStart, int numEnd) {
this.numStart = numStart;
this.numEnd = numEnd;
}
// 声明后继者(上级领导)
private Handler nextHandler;
// 各级领导处理请假条的方法
protected abstract void handleLeave(LeaveRequest leave);
// 提交请假条(不能被继承)
public final void submit(LeaveRequest leave) {
// 该领导审批
handleLeave(leave);
if(nextHandler != null && leave.getNum() > numEnd) {
// 交给上级领导审批
nextHandler.handleLeave(leave);
} else {
System.out.println("流程结束");
}
}
}
小组长类、部门经理类、总经理类(具体的处理者)
public class GroupLeader extends Handler {
public GroupLeader() {
super(0, Handler.NUM_ONE);
}
@Override
protected void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天,原因:" + leave.getContent());
System.out.println("小组长审批:同意");
}
}
public class Manager extends Handler {
public Manager() {
super(Handler.NUM_ONE, Handler.NUM_THREE);
}
@Override
protected void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天,原因:" + leave.getContent());
System.out.println("部门经理审批:同意");
}
}
public class GeneralManager extends Handler {
public GeneralManager() {
super(Handler.NUM_THREE, Handler.NUM_SEVEN);
}
@Override
protected void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天,原因:" + leave.getContent());
System.out.println("总经理类审批:同意");
}
}
测试类:
public class Client {
public static void main(String[] args) {
// 1. 创建请假条对象
LeaveRequest leave = new LeaveRequest("小明", 1, "身体不适");
// 2. 创建各级领导对象
GroupLeader groupLeader = new GroupLeader();
Manager manager = new Manager();
GeneralManager generalManager = new GeneralManager();
// 3. 设置处理者链
groupLeader.setNextHandler(manager);
manager.setNextHandler(generalManager);
// 4. 小明提交请假
groupLeader.submit(leave); // 调用小组长里的submit()方法
}
}
降低了请求发送者 和 请求接收者的耦合度
每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成