设计模式二三事
总第493 篇
2022年 第010篇
设计模式是众多软件开发人员经过长时间的试错和应用总结出来的,解决特定问题的一系列方案。现行的部分教材在介绍设计模式时,有些会因为案例脱离实际应用场景而令人费解,有些又会因为场景简单而显得有些小题大做。
-
引言
-
奖励的发放策略
-
任务模型的设计
-
活动的迭代重构
-
结语
-
参考资料
-
作者简介
引言
奖励的发放策略
// 奖励服务class RewardService { // 外部服务 private WaimaiService waimaiService; private HotelService hotelService; private FoodService foodService; // 使用对入参的条件判断进行发奖 public void issueReward(String rewardType, Object ... params) { if ("Waimai".equals(rewardType)) { WaimaiRequest request = new WaimaiRequest(); // 构建入参 request.setWaimaiReq(params); waimaiService.issueWaimai(request); } else if ("Hotel".equals(rewardType)) { HotelRequest request = new HotelRequest(); request.addHotelReq(params); hotelService.sendPrize(request); } else if ("Food".equals(rewardType)) { FoodRequest request = new FoodRequest(params); foodService.getCoupon(request); } else { throw new IllegalArgumentException("rewardType error!"); } }}
策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。策略模式通常包含以下角色:
抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
适配器模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式包含以下主要角色:
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
// 策略接口interface Strategy { void issue(Object ... params);}// 外卖策略class Waimai implements Strategy { private WaimaiService waimaiService; @Override public void issue(Object... params) { WaimaiRequest request = new WaimaiRequest(); // 构建入参 request.setWaimaiReq(params); waimaiService.issueWaimai(request); }}// 酒旅策略class Hotel implements Strategy { private HotelService hotelService; @Override public void issue(Object... params) { HotelRequest request = new HotelRequest(); request.addHotelReq(params); hotelService.sendPrize(request); }}// 美食策略class Food implements Strategy { private FoodService foodService; @Override public void issue(Object... params) { FoodRequest request = new FoodRequest(params); foodService.payCoupon(request); }}
// 使用分支判断获取的策略上下文class StrategyContext { public static Strategy getStrategy(String rewardType) { switch (rewardType) { case "Waimai": return new Waimai(); case "Hotel": return new Hotel(); case "Food": return new Food(); default: throw new IllegalArgumentException("rewardType error!"); } }}// 优化后的策略服务class RewardService { public void issueReward(String rewardType, Object ... params) { Strategy strategy = StrategyContext.getStrategy(rewardType); strategy.issue(params); }}
单例模式设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
// 策略上下文,用于管理策略的注册和获取class StrategyContext { private static final Map<String, Strategy> registerMap = new HashMap<>(); // 注册策略 public static void registerStrategy(String rewardType, Strategy strategy) { registerMap.putIfAbsent(rewardType, strategy); } // 获取策略 public static Strategy getStrategy(String rewardType) { return registerMap.get(rewardType); }}// 抽象策略类abstract class AbstractStrategy implements Strategy { // 类注册方法 public void register() { StrategyContext.registerStrategy(getClass().getSimpleName(), this); }}// 单例外卖策略class Waimai extends AbstractStrategy implements Strategy { private static final Waimai instance = new Waimai(); private WaimaiService waimaiService; private Waimai() { register(); } public static Waimai getInstance() { return instance; } @Override public void issue(Object... params) { WaimaiRequest request = new WaimaiRequest(); // 构建入参 request.setWaimaiReq(params); waimaiService.issueWaimai(request); }}// 单例酒旅策略class Hotel extends AbstractStrategy implements Strategy { private static final Hotel instance = new Hotel(); private HotelService hotelService; private Hotel() { register(); } public static Hotel getInstance() { return instance; } @Override public void issue(Object... params) { HotelRequest request = new HotelRequest(); request.addHotelReq(params); hotelService.sendPrize(request); }}// 单例美食策略class Food extends AbstractStrategy implements Strategy { private static final Food instance = new Food(); private FoodService foodService; private Food() { register(); } public static Food getInstance() { return instance; } @Override public void issue(Object... params) { FoodRequest request = new FoodRequest(params); foodService.payCoupon(request); }}
@Component
和
@PostConstruct
注解即可完成单例的创建和注册,代码会更加简洁。
任务模型的设计
// 任务状态枚举@AllArgsConstructor@Getterenum TaskState { INIT("初始化"), ONGOING( "进行中"), PAUSED("暂停中"), FINISHED("已完成"), EXPIRED("已过期") ; private final String message;}// 行为枚举@AllArgsConstructor@Getterenum ActionType { START(1, "开始"), STOP(2, "暂停"), ACHIEVE(3, "完成"), EXPIRE(4, "过期") ; private final int code; private final String message;}
class Task { private Long taskId; // 任务的默认状态为初始化 private TaskState state = TaskState.INIT; // 活动服务 private ActivityService activityService; // 任务管理器 private TaskManager taskManager; // 使用条件分支进行任务更新 public void updateState(ActionType actionType) { if (state == TaskState.INIT) { if (actionType == ActionType.START) { state = TaskState.ONGOING; } } else if (state == TaskState.ONGOING) { if (actionType == ActionType.ACHIEVE) { state = TaskState.FINISHED; // 任务完成后进对外部服务进行通知 activityService.notifyFinished(taskId); taskManager.release(taskId); } else if (actionType == ActionType.STOP) { state = TaskState.PAUSED; } else if (actionType == ActionType.EXPIRE) { state = TaskState.EXPIRED; } } else if (state == TaskState.PAUSED) { if (actionType == ActionType.START) { state = TaskState.ONGOING; } else if (actionType == ActionType.EXPIRE) { state = TaskState.EXPIRED; } } }}
updateState
方法中完成了2个重要的功能:
-
接收不同的行为,然后更新当前任务的状态; -
当任务过期时,通知任务所属的活动和任务管理器。
状态模式:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。状态模式包含以下主要角色:
环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
// 任务状态抽象接口interface State { // 默认实现,不做任何处理 default void update(Task task, ActionType actionType) { // do nothing }}// 任务初始状态class TaskInit implements State { @Override public void update(Task task, ActionType actionType) { if (actionType == ActionType.START) { task.setState(new TaskOngoing()); } }}// 任务进行状态class TaskOngoing implements State { private ActivityService activityService; private TaskManager taskManager; @Override public void update(Task task, ActionType actionType) { if (actionType == ActionType.ACHIEVE) { task.setState(new TaskFinished()); // 通知 activityService.notifyFinished(taskId); taskManager.release(taskId); } else if (actionType == ActionType.STOP) { task.setState(new TaskPaused()); } else if (actionType == ActionType.EXPIRE) { task.setState(new TaskExpired()); } }}// 任务暂停状态class TaskPaused implements State { @Override public void update(Task task, ActionType actionType) { if (actionType == ActionType.START) { task.setState(new TaskOngoing()); } else if (actionType == ActionType.EXPIRE) { task.setState(new TaskExpired()); } }}// 任务完成状态class TaskFinished implements State {}// 任务过期状态class TaskExpired implements State {}@Dataclass Task { private Long taskId; // 初始化为初始态 private State state = new TaskInit(); // 更新状态 public void updateState(ActionType actionType) { state.update(this, actionType); }}
观察者模式:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。观察者模式的主要角色如下。
抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
// 抽象观察者interface Observer { void response(Long taskId); // 反应}// 抽象目标abstract class Subject { protected List<Observer> observers = new ArrayList<Observer>(); // 增加观察者方法 public void add(Observer observer) { observers.add(observer); } // 删除观察者方法 public void remove(Observer observer) { observers.remove(observer); } // 通知观察者方法 public void notifyObserver(Long taskId) { for (Observer observer : observers) { observer.response(taskId); } }}// 活动观察者class ActivityObserver implements Observer { private ActivityService activityService; @Override public void response(Long taskId) { activityService.notifyFinished(taskId); }}// 任务管理观察者class TaskManageObserver implements Observer { private TaskManager taskManager; @Override public void response(Long taskId) { taskManager.release(taskId); }}
// 任务进行状态class TaskOngoing extends Subject implements State { @Override public void update(Task task, ActionType actionType) { if (actionType == ActionType.ACHIEVE) { task.setState(new TaskFinished()); // 通知 notifyObserver(task.getTaskId()); } else if (actionType == ActionType.STOP) { task.setState(new TaskPaused()); } else if (actionType == ActionType.EXPIRE) { task.setState(new TaskExpired()); } }}// 任务初始状态class TaskInit implements State { @Override public void update(Task task, ActionType actionType) { if (actionType == ActionType.START) { TaskOngoing taskOngoing = new TaskOngoing(); taskOngoing.add(new ActivityObserver()); taskOngoing.add(new TaskManageObserver()); task.setState(taskOngoing); } }}
活动的迭代重构
// 抽象活动接口interface ActivityInterface { void participate(Long userId);}// 活动类class Activity implements ActivityInterface { private String type; private Long id; private String name; private Integer scene; private String material; public Activity(String type) { this.type = type; // id的构建部分依赖于活动的type if ("period".equals(type)) { id = 0L; } } public Activity(String type, Long id) { this.type = type; this.id = id; } public Activity(String type, Long id, Integer scene) { this.type = type; this.id = id; this.scene = scene; } public Activity(String type, String name, Integer scene, String material) { this.type = type; this.scene = scene; this.material = material; // name的构建完全依赖于活动的type if ("period".equals(type)) { this.id = 0L; this.name = "period" + name; } else { this.name = "normal" + name; } } // 参与活动 @Override public void participate(Long userId) { // do nothing }}// 任务型活动class TaskActivity extends Activity { private Task task; public TaskActivity(String type, String name, Integer scene, String material, Task task) { super(type, name, scene, material); this.task = task; } // 参与任务型活动 @Override public void participate(Long userId) { // 更新任务状态为进行中 task.getState().update(task, ActionType.START); }}
-
活动的构造组件较多,导致可以组合的构造函数太多,尤其是在模型增加字段时还需要去修改构造函数; -
部分组件的构造存在一定的顺序关系,但是当前的实现没有体现顺序,导致构造逻辑比较混乱,并且存在部分重复的代码。
建造者模式:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。建造者模式的主要角色如下:
产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
// 活动类class Activity implements ActivityInterface { protected String type; protected Long id; protected String name; protected Integer scene; protected String material; // 全参构造函数 public Activity(String type, Long id, String name, Integer scene, String material) { this.type = type; this.id = id; this.name = name; this.scene = scene; this.material = material; } @Override public void participate(Long userId) { // do nothing } // 静态建造器类,使用奇异递归模板模式允许继承并返回继承建造器类 public static class Builder<T extends Builder<T>> { protected String type; protected Long id; protected String name; protected Integer scene; protected String material; public T setType(String type) { this.type = type; return (T) this; } public T setId(Long id) { this.id = id; return (T) this; } public T setId() { if ("period".equals(this.type)) { this.id = 0L; } return (T) this; } public T setScene(Integer scene) { this.scene = scene; return (T) this; } public T setMaterial(String material) { this.material = material; return (T) this; } public T setName(String name) { if ("period".equals(this.type)) { this.name = "period" + name; } else { this.name = "normal" + name; } return (T) this; } public Activity build(){ return new Activity(type, id, name, scene, material); } }}// 任务型活动class TaskActivity extends Activity { protected Task task; // 全参构造函数 public TaskActivity(String type, Long id, String name, Integer scene, String material, Task task) { super(type, id, name, scene, material); this.task = task; } // 参与任务型活动 @Override public void participate(Long userId) { // 更新任务状态为进行中 task.getState().update(task, ActionType.START); } // 继承建造器类 public static class Builder extends Activity.Builder<Builder> { private Task task; public Builder setTask(Task task) { this.task = task; return this; } public TaskActivity build(){ return new TaskActivity(type, id, name, scene, material, task); } }}
public void participate(Long userId) { // 对目标用户做风险控制,失败则抛出异常 Risk.doControl(userId); // 更新任务状态为进行中 task.state.update(task, ActionType.START);}
装饰器模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。装饰器模式主要包含以下角色:
抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
// 抽象装饰角色abstract class ActivityDecorator implements ActivityInterface { protected ActivityInterface activity; public ActivityDecorator(ActivityInterface activity) { this.activity = activity; } public abstract void participate(Long userId);}// 能够对活动做风险控制的包装类class RiskControlDecorator extends ActivityDecorator { public RiskControlDecorator(ActivityInterface activity) { super(activity); } @Override public void participate(Long userId) { // 对目标用户做风险控制,失败则抛出异常 Risk.doControl(userId); // 更新任务状态为进行中 activity.participate(userId); }}
结语
参考资料
-
Gamma E. 设计模式: 可复用面向对象软件的基础 [M]. 机械工业出版社, 2007. -
弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007. -
oodesign.com -
java-design-patterns.com -
Java设计模式:23种设计模式全面解析
作者简介
阅读更多