什么是开闭原则?
开闭原则的英文是Open Closed Principle
,简称OCP
,它的英文描述是:
Software entities (modules, classes, functions, etc.) should be open for extension, but closed for modification.
软件实体(模块、类、方法等)应该『对扩展开放、对修改关闭』。
通俗的说,在添加一个新的功能时,应该是在已有的代码基础上扩展代码(如新增模块、类、方法等),而非修改已有的代码。
举个栗子:
下面是一个API接口监控警告的代码,AlertRule
存储警告规则,Notification
是告警通知类。业务逻辑主要集中在check()
函数。
public class Alert {
private AlertRule rule;
private Notification notification;
public Alert(AlertRule rule, Notification notification) {
this.rule = rule;
this.notification = notification;
}
public void check(String api, long requestCount, long errorCount, long durationOfSeconds) {
long tps = requestCount / durationOfSeconds;
if (tps > rule.getMatchedRule(api).getMaxTps()) {
notification.notify(NotificationEmergencyLevel.URGENCY, "...");
}
if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {
notification.notify(NotificationEmergencyLevel.SEVERE, "...");
}
}
}
现在,如果我要添加一个功能:当每秒接口超时请求个数,超过某个预先设置的最大阈值时,就报警。 我们可以直接在check()
函数里修改入参,添加timeoutCount
,然后在函数内部添加if
分支。
但如果后面需要添加的规则越来越多,就多导致函数的参数很多,管理其他很麻烦。
我们可以重构一下:
-
将
check()
函数的多个入参封装为ApiStatInfo类
-
引入
handler
的概念,将if逻辑分散在各个handler
中
public class Alert {
private List<AlertHandler> alertHandlers = new ArrayList<>();
public void addAlertHandler(AlertHandler alertHandler) {
this.alertHandlers.add(alertHandler);
}
public void check(ApiStatInfo apiStatInfo) {
for (AlertHandler handler : alertHandlers) {
handler.check(apiStatInfo);
}
}
}
public class ApiStatInfo {//省略constructor/getter/setter方法
private String api;
private long requestCount;
private long errorCount;
private long durationOfSeconds;
}
public abstract class AlertHandler {
protected AlertRule rule;
protected Notification notification;
public AlertHandler(AlertRule rule, Notification notification) {
this.rule = rule;
this.notification = notification;
}
public abstract void check(ApiStatInfo apiStatInfo);
}
public class TpsAlertHandler extends AlertHandler {
public TpsAlertHandler(AlertRule rule, Notification notification) {
super(rule, notification);
}
@Override
public void check(ApiStatInfo apiStatInfo) {
long tps = apiStatInfo.getRequestCount()/ apiStatInfo.getDurationOfSeconds();
if (tps > rule.getMatchedRule(apiStatInfo.getApi()).getMaxTps()) {
notification.notify(NotificationEmergencyLevel.URGENCY, "...");
}
}
}
public class ErrorAlertHandler extends AlertHandler {
public ErrorAlertHandler(AlertRule rule, Notification notification){
super(rule, notification);
}
@Override
public void check(ApiStatInfo apiStatInfo) {
if (apiStatInfo.getErrorCount() > rule.getMatchedRule(apiStatInfo.getApi()).getMaxErrorCount()) {
notification.notify(NotificationEmergencyLevel.SEVERE, "...");
}
}
}
重构之后,可以通过ApplicationContext
单例类,对Alert进行创建和组装。
public class Alert { // 代码未改动... }
public class ApiStatInfo {//省略constructor/getter/setter方法
private String api;
private long requestCount;
private long errorCount;
private long durationOfSeconds;
private long timeoutCount; // 改动一:添加新字段
}
public abstract class AlertHandler { //代码未改动... }
public class TpsAlertHandler extends AlertHandler {//代码未改动...}
public class ErrorAlertHandler extends AlertHandler {//代码未改动...}
// 改动二:添加新的handler
public class TimeoutAlertHandler extends AlertHandler {//省略代码...}
public class ApplicationContext {
private AlertRule alertRule;
private Notification notification;
private Alert alert;
public void initializeBeans() {
alertRule = new AlertRule(/*.省略参数.*/); //省略一些初始化代码
notification = new Notification(/*.省略参数.*/); //省略一些初始化代码
alert = new Alert();
alert.addAlertHandler(new TpsAlertHandler(alertRule, notification));
alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));
// 改动三:注册handler
alert.addAlertHandler(new TimeoutAlertHandler(alertRule, notification));
}
//...省略其他未改动代码...
}
public class Demo {
public static void main(String[] args) {
ApiStatInfo apiStatInfo = new ApiStatInfo();
// ...省略apiStatInfo的set字段代码
apiStatInfo.setTimeoutCount(289); // 改动四:设置tiemoutCount值
ApplicationContext.getInstance().getAlert().check(apiStatInfo);
}
开闭原则有什么好处?
开闭原则实际上是对基于接口或抽象实现『封闭』,基于实现接口或继承实现『开放』的阐释。对扩展开发是为了应为需求变化,对修改关闭时为了保证已有代码的稳定性,最终让系统更具有弹性。
开闭原则并不是免费的,有些情况下,代码的扩展性与可读性是互相冲突的。在某些场景下,代码的扩展性很重要,我们就可以适当牺牲一些代码可读性;在另一些场景下,代码的可读性更重要,我们就需要牺牲一定的代码可扩展性。
如何践行开闭原则?
-
时刻具备扩展意识、抽象意识、封装意识
-
往前多思考、预先留好扩展点
-
识别可变和不可变部分,将可变部分封装,隔离变化,提供抽象的不可变接口
-
许多设计原则、设计思想、设计模式都是为提高代码扩展性而生的
回顾一下
开闭原则的英文是Open Closed Principle
,简称OCP
。在添加一个新的功能时,应该是在已有的代码基础上扩展代码(如新增模块、类、方法等),而非修改已有的代码。我们要时刻具备扩展意识、抽象意识、封装意识,识别可变和不可变部分,将可变部分封装,隔离变化,提供抽象的不可变接口。