设计模式——职责链模式
一、前言
设计模式是指导一个程序猿以更好的姿态处理一些问题,而不再像刚学编程的我们,只会使用if-else分支语句,或是使用硬干的骚操作完成需求。不使用设计模式,一来是代码逻辑会越来越晦涩难懂(到了某天你会发现自己也没办法看清楚所有逻辑),二来是代码维护成本越来越高(你的加班时间会越来越长),三来是可以装bility。基于这么多的好处,小盆友和大家一起讨论和分享下设计模式,让自己不再是一个坐在电脑前敲代码的码农。
二、讲个小故事——“部门请假”
今天我们来讨论下职责链模式,但在详细讲解前,我们先来看个场景。
在工作中,请假是在所难免,有时是比较短时间的请假,例如:请一天假,陪陪女朋友逛街,这个时候只需要请示下部门组长;请三天假,去一个短暂的徒步旅程,这个时候需要请示下部门经理;请五天假,回家陪陪父母,这个时候需要请示下CTO。
在没有任何模式指导的情况下,我们会这么来写这个场景:
public class NoDesign {
public static void main(String[] args) {
int level = 1;
if (level <= 1) {
System.out.println("我是组长,批准!");
} else if (level <= 3) {
System.out.println("我是部门经理,批准!");
} else if (level <= 5) {
System.out.println("我是CTO,批准!");
} else {
System.out.println("没人处理,不批准!");
}
}
}
大家会发现,这是一段有臭味的代码,一旦业务需求有变,修改人员(有可能是新接手的程序猿)需要去阅读整串代码,然后进行修改。如果逻辑非常复杂,有可能会有牵一发动全身的问题。换个装bility的说法,也就是职责不分明,引起这个类的变化的原因不止一个;而且也违背了开闭原则,扩展的时候需要改到之前的类。总之,需要重构。
三、如何进行重构
这种由请求者发起请求,处理者们根据不同的条件进行处理的场景(例如:普通客户—VIP—SVIP)。其实可以考虑用职责链模式来进行处理。
以下便是职责链的通用类图,现在我们就把刚才的场景一一对照,提出请假的人便是Client,而组长、经理、CTO三者其实是同一类人,即都是请求处理者(ConcreteHandler),而他们都是抽象的处理者(Handler)的子类。
按照通用类图进行重构后,类图如下,分为五个类:
- Client: 调用者;
- Request: 请求体,用于封装一些请求的信息,有时候判断的因素不只是一个;
- Response: 结果实体,消息处理完封装的结果类;
- Handler: 处理请求的实体类;
- Level: 等级类,用于保存当前等级的一些消息,如果逻辑简单可以和Request类合并;
四、编码时刻
说了那么多,主要还是编码,我们先来看Handler:
public abstract class Handler {
//指向下一个处理类
private Handler nextHandler;
public final Response handleMessage(Request request) {
Response response = null;
//判断是否是自己的处理等级
if (request.getLevel().getDay() <= this.getHandlerLevel().getDay()) {
//是自己的处理等级,就将其进行处理
response = this.echo(request);
} else {
//如果还有下一个handler
if (this.nextHandler != null) {
//如果有就让下一个handler进行处理
response = this.nextHandler.handleMessage(request);
} else {
//没有适当的处理者,由调用者自己处理,获取不处理
}
}
return response;
}
/**
* @date 创建时间 2018/3/28
* @author Jiang zinc
* @Description 设置下一个handler处理类
* @version
*/
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
/**
* @date 创建时间 2018/3/28
* @author Jiang zinc
* @Description 模版方法,由子类决定当前实现的实体类是什么等级
* @version
*/
protected abstract Level getHandlerLevel();
/**
* @date 创建时间 2018/3/28
* @author Jiang zinc
* @Description 对任务的具体处理操作
* @version
*/
protected abstract Response echo(Request request);
}
public class CTOHandler extends Handler {
@Override
public Level getHandlerLevel() {
return new Level("技术总监", 5);
}
@Override
public Response echo(Request request) {
return new Response("我是"+getHandlerLevel().getLevelName()+",我准了!");
}
}
public class ManagerHandler extends Handler {
@Override
public Level getHandlerLevel() {
return new Level("经理",3);
}
@Override
public Response echo(Request request) {
return new Response("我是"+getHandlerLevel().getLevelName()+",我准了!");
}
}
public class TeamLeaderHandler extends Handler {
@Override
public Level getHandlerLevel() {
return new Level("组长", 1);
}
@Override
public Response echo(Request request) {
return new Response("我是"+getHandlerLevel().getLevelName()+",我准了!");
}
}
在Handler中,由handleMessage方法进行判断是否交由当前的handler进行处理,在这个抽象类中使用了模版方法(getHandlerLevel和echo),将Handler的等级和处理逻辑延迟到子类进行处理。所以子类的handler只需要处理好当前自己等级的逻辑即可,然后基类的handler做好链的调用逻辑处理(即什么情况交由当前handler,什么情况传递下一个处理),最后等待拼装成链给到用户(即例子中的请假人)使用。
接下来看下请求类、结果类、等级类:
public class Request {
private Level mLevel;
public Request(Level mLevel) {
this.mLevel = mLevel;
}
public Level getLevel() {
return this.mLevel;
}
}
public class Response {
private String content;
public Response(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public class Level {
private String levelName;
private int day;
public Level(String levelName, int day) {
this.levelName = levelName;
this.day = day;
}
public Level(int day) {
this.day = day;
}
public String getLevelName() {
return levelName;
}
public void setLevelName(String levelName) {
this.levelName = levelName;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
}
这几个类比较简单,只是常规的bean,在真正的使用中,可能会减少也有可能会增多,这个视情况而定。
最后就是使用职责链了,这里使用了一个ResponChainHelper辅助类,进行职责链的拼装,让client无需知道拼装过程。从client类中,可看到,这时只需要获取到职责链的handler,实例化一个request,交由handler处理即可,至于谁处理,对于用于来说是透明的。
public class ResponChainHelper {
public static Handler getResChain() {
Handler ctoHandler = new CTOHandler();
Handler managerHandler = new ManagerHandler();
Handler teamLeaderHandler = new TeamLeaderHandler();
teamLeaderHandler.setNextHandler(managerHandler);
managerHandler.setNextHandler(ctoHandler);
return teamLeaderHandler;
}
}
public class Client {
public static void main(String[] args) {
Handler handler = ResponChainHelper.getResChain();
System.out.println("我想要请一天假:");
Request request1 = new Request(new Level(1));
Response response1 = handler.handleMessage(request1);
System.out.println(response1.getContent());
System.out.println("我想要请三天假:");
Request request2 = new Request(new Level(3));
Response response2 = handler.handleMessage(request2);
System.out.println(response2.getContent());
System.out.println("我想要请五天假:");
Request request3 = new Request(new Level(5));
Response response3 = handler.handleMessage(request3);
System.out.println(response3.getContent());
}
}
五、如何扩展
如果此时,需求变动,需要增加一项:请假10天的需要CEO批准(这里只是打个比方,现实的CEO很忙不会有空管理这些事),只需要继承handler,然后编写自己需要处理的逻辑,最后在helper中加入到指责链,其余的所有代码都无需改动。
public class CEOHandler extends Handler {
@Override
protected Level getHandlerLevel() {
return new Level("CEO", 10);
}
@Override
protected Response echo(Request request) {
return new Response("我是" + getHandlerLevel().getLevelName() + ",我准了!");
}
}
public class ResponChainHelper {
public static Handler getResChain() {
Handler ctoHandler = new CTOHandler();
Handler managerHandler = new ManagerHandler();
Handler teamLeaderHandler = new TeamLeaderHandler();
Handler ceoHandler = new CEOHandler();
teamLeaderHandler.setNextHandler(managerHandler);
managerHandler.setNextHandler(ctoHandler);
ctoHandler.setNextHandler(ceoHandler);
return teamLeaderHandler;
}
}
六、职责链模式的缺点
- 性能问题:每次请求都会从职责链的链头开始处理,当职责链比较长的时候,性能就会成为一个问题。
- 调试不便:假设某个环节除了什么问题,当职责链比较长时,需要确定是哪个handler出现问题是比较麻烦的,因为职责链有些类似递归处理,过长的链,层级的增加会导致逻辑复杂。
如何避免这些缺点?主要通过职责链的长度,以阀值来限制职责链的长度。
七、写在最后
本篇文章没有长篇大论的讲理论,因为这不是我的风格,更多的是通过前后的代码对比和一些编码时微妙的变化入手,如果这篇文章对你有所启发请给个“❤️”鼓励下吧。