OOP 七大原则
- 开闭原则
对扩展开放,对修改关闭。
- 里氏替换原则
继承必须确保超类(父类)所拥有的性质在子类中仍然成立。
- 依赖倒置原则
要面向接口编程,而不是面向实现编程。
- 单一职责原则
控制类的粒度大小、将对象解耦、提高其内聚性。
- 接口隔离原则
要为每个类建立它们需要的专用接口。
- 迪米特法则
解耦,只与你的直接朋友交谈,不跟“陌生人”说话。
- 合成复用原则
尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
奖励的发放策略
假设现在要做一个营销,需要用户参与一个活动,然后完成一系列的任务,最后可以得到一些奖励作为回报。活动的奖励包含美团外卖、酒旅和美食等多种品类券,现在需要你帮忙设计一套奖励发放方案。
原始 Demo:
// 奖励服务
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)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者
-
优化 Demo:
// 策略接口
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);
}
}
代码耦合度降低,但仍存在以下问题:
- 策略类是有状态的模型吗?如果不是是否可以考虑做成单例的;
- 环境类的获取策略方法职责很明确,但是依然没有做到完全对修改封闭
使用单例模式来优化
-
单例模式
- 创建型模式,它提供了一种创建对象的最佳方式;
- 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象
// 策略接口
interface Strategy {
void issue(Object ... 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);
}
}
Strategy strategy = StrategyContext.getStrategy("Waimai");
strategy.issue("外卖详情参数");
// Waimai waimai = Waimai.getInstance();
// waimai.issue("外卖详情参数");
任务模型的设计
完成任务模型的设计。你需要重点关注状态的流转变更,以及状态变更后的消息通知。
原始 Demo:
// 任务状态枚举
@AllArgsConstructor
@Getter
enum TaskState {
INIT("初始化"),
ONGOING( "进行中"),
PAUSED("暂停中"),
FINISHED("已完成"),
EXPIRED("已过期");
private final String message;
}
// 行为枚举
@AllArgsConstructor
@Getter
enum 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;
}
}
}
}
存在问题:
- 方法中使用条件判断来控制语句,但是当条件复杂或者状态太多时,条件判断语句会过于臃肿,可读性差,且不具备扩展性,维护难度也大。且增加新的状态时要添加新的 if-else 语句,这违背了开闭原则,不利于程序的扩展;
- 任务类不够高内聚,它在通知实现中感知了其他领域或模块的模型,如活动和任务管理器,这样代码的耦合度太高,不利于扩展
使用状态模式 & 观察者模式来优化
-
状态模式
-
对有状态的对象,把复杂的「判断逻辑」提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为;
-
状态模式包含以下主要角色:
- 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换;
- 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为;
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换
-
-
观察者模式
-
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作「发布-订阅」模式、「模型-视图」模式,它是对象行为型模式;
-
观察者模式的主要角色如下:
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法;
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象;
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用;
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态
-
// 任务状态枚举
@AllArgsConstructor
@Getter
enum TaskState {
INIT("初始化"),
ONGOING( "进行中"),
PAUSED("暂停中"),
FINISHED("已完成"),
EXPIRED("已过期");
private final String message;
}
// 行为枚举
@AllArgsConstructor
@Getter
enum ActionType {
START(1, "开始"),
STOP(2, "暂停"),
ACHIEVE(3, "完成"),
EXPIRE(4, "过期");
private final int code;
private final String message;
}
// 任务状态抽象接口
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 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);
}
}
}
// 任务进行状态
// 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 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 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 {
// ...
}
@Data
class Task {
private Long taskId;
// 初始化为初始态
private State state = new TaskInit();
// 更新状态
public void updateState(ActionType actionType) {
state.update(this, actionType);
}
}
// 抽象观察者
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);
}
}
活动的迭代重构
之前设计开发了活动模型,现在我们需要在任务型活动的参与方法上增加一层风险控制。
原始 Demo:
// 抽象活动接口
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):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息
-
-
装饰器模式
-
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式;
-
装饰器模式主要包含以下角色:
- 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象;
- 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责;
- 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能;
- 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任
-
// 抽象活动接口
interface ActivityInterface {
void participate(Long userId);
}
// 活动类
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 void participate(Long userId) {
// // 对目标用户做风险控制, 失败则抛出异常
// Risk.doControl(userId);
// // 更新任务状态为进行中
// task.state.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);
}
}
}
// 抽象装饰角色
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);
}
}
TaskActivity taskActivity = new TaskActivity( /* ... */ );
// 对TaskActivity对象进行风险控制包装
ActivityInterface wrappedTaskActivity = new RiskControlDecorator(taskActivity);
wrappedTaskActivity.participate(userId);
参考资料
- 美团技术团队公众号:https://mp.weixin.qq.com/s/H2toewJKEwq1mXme_iMWkA