24.加薪非要老总批? - 职责连模式

24.1 老板, 我要加薪!

  • 时间: 7月2日20点   地点: 小菜大鸟住所的客厅   人物: 小菜大鸟

     “大鸟, 你说我现在干满三个月了, 马上要办转正手续, 我提提加薪的事情好不好?” 小菜问道.
     “这要看你这三个月做得如何了.”
     …..

  • 时间: 7月3日19点   地点: 小菜大鸟住所的客厅   人物: 小菜大鸟

     “小菜, 是不是该去吃龙虾了?” 大鸟下班回来问道.
     “嗨? 别提了, 不让加.”
     “嗯? 为什么?”
     ….
     “你把今天你向经理申请, 经理没权利, 然后向总监上报, 总监也没权限, 向总经理上报的事, 写成代码看看吧, 注意哦, 不一定是加薪, 还有可能是休假申请.”
     “哦, 难道这也是模式? 我试试看.”

24.2 加薪代码初步

     “实现这个场景感觉有些难度哦!”
     “首先我觉得无论加薪还是请假, 都是一种申请. 申请就应该有申请的类别, 申请内容, 和申请数量.”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Request {
// 申请类别
private String requestType;
// 申请内容
private String requestContent;
// 数量
private int number;
public String getRequestType() { return requestType; }
public void setRequestType(String requestType) { this.requestType = requestType; }
public String getRequestContent() { return requestContent; }
public void setRequestContent(String requestContent) { this.requestContent = requestContent; }
public int getNumber() { return number; }
public void setNumber(int number) { this.number = number; }
}

     “然后我觉得经理, 总监, 总经理都是管理者, 他们在对 ‘申请’ 处理时, 需要作出判断, 是否有决策权.”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 管理者
public class Manager {
protected String name;
public Manager(String name) {
this.name = name;
}
// 长的方法, 多条分支, 这些都是代码的坏味道
public void getResult(String managerLevel, Request request) {
if (managerLevel.equals("经理")) {
if (request.getRequestType().equals("请假") && request.getNumber() < 2) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 被批准");
} else {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 我无权处理");
}
} else if (managerLevel.equals("总监")) {
if (request.getRequestType().equals("请假") && request.getNumber() <= 5) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 被批准");
} else {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 我无权处理");
}
} else if (managerLevel.equals("总经理")) {
if (request.getRequestType().equals("请假")) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 被批准");
} else if (request.getRequestType().equals("加薪") && request.getNumber() <= 500) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 被批准");
} else if (request.getRequestType().equals("加薪") && request.getNumber() > 500) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 再说吧");
}
}
}
}

     “其实我自己也觉得写的不好.” 小菜说道, “但是也不知道怎么去处理这类问题.”
     “那里写的不好?” 大鸟问道.
     “那个 ‘管理者’ 类吧, 里面的 ‘结果’ 方法比较长, 加上有太多的分支判断, 这其实是非常不好的设计.”
     “说的不错, 因为你很难讲当中还会不会增加其他的管理级别, 比如项目经理, 部门经理, 人力总监, 副总经理等等. 那就意味着都需要去更改这个类, 这个类承担了太多的责任, 这违背了那些设计原则.”
     “类有太多的责任, 这违背了, 单一职责原则, 增加新的管理类别, 需要修改这个类, 违背了开放—封闭原则.”
     “说得好, 那你觉得应该如何下手去重构呢?”
     “你刚才提到了可能会增加管理级别, 那就意味着这里容易变化, 我想把这些公司管理者的类别各自做成管理者的子类, 这就可以利用多态性来化解分支带来的僵化.”
     “那如何解决经理无权上报总监, 总监无权上报总经理这样的功能呢?”
     “我想他们之间一定有关联, 把用户的请求传递, 只到可以解决这个请求为止.”
     “说的不错, 你其实已经说到一个行为设计模式 ‘职责链模式‘ 的意图了.”

24.2 职责链模式

职责链模式(Chain of Responsibility): 使多个对象都有机会处理请求, 从而避免请求的发送者与请求的接受者之间的耦合关系. 将这个对象连成一条链, 并沿着这条链传递该请求, 直到有一个对象处理它为止.

     “这里发出请求的客户端并不知道这当中的那一个对象处理这个请求, 这样系统的更改可以在不影响客户端的情况下去动态的重新组织和分配责任.”
     “我们来看看结构图.”



     Handler 类, 定义一个处理请示的接口

1
2
3
4
5
6
7
8
9
10
public abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
// 处理请求的抽象方法
public abstract void handleRequest(int request);
}

     ConcreteHandler 类, 具体处理者类, 处理它所负责的请求, 可访问他的后继者, 如果处理该请求, 就处理之, 否则就将请求发送给他的后继者.
     ConcreteHandler1, 当请求数在0到10之间则有权处理, 否则转到下一位.

1
2
3
4
5
6
7
8
9
10
11
public class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(int request) {
// 0 到 10 处理请求
if (request >= 0 && request < 10) {
System.out.println(getClass().getSimpleName() + "处理请求" + request);
} else if (successor != null) { // 转到下一位
successor.handleRequest(request);
}
}
}

     ConcreteHandler2, 当请求数在10到20之间则有权处理, 否则转到下一位.

1
2
3
4
5
6
7
8
9
10
11
public class ConcreteHandler2 extends Handler {
@Override
public void handleRequest(int request) {
// 10 到 20 处理请求
if (request >= 10 && request < 20) {
System.out.println(getClass().getSimpleName() + "处理请求" + request);
} else if (successor != null) { // 转到下一位
successor.handleRequest(request);
}
}
}

     ConcreteHandler3, 当请求数在20到30之间则有权处理, 否则转到下一位.

1
2
3
4
5
6
7
8
9
10
11
public class ConcreteHandler3 extends Handler {
@Override
public void handleRequest(int request) {
// 20 到 30 处理请求
if (request >= 20 && request < 30) {
System.out.println(getClass().getSimpleName() + "处理请求" + request);
} else if (successor != null) { // 转到下一位
successor.handleRequest(request);
}
}
}

     客户端代码, 向链上的具体处理者对象提交请求.

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
Handler h1 = new ConcreteHandler1();
Handler h2 = new ConcreteHandler2();
Handler h3 = new ConcreteHandler3();
h1.setSuccessor(h2);
h2.setSuccessor(h3);
int[] requests = {2, 5, 14, 22, 18, 2, 37, 20};
for (int request : requests) {
h1.handleRequest(request);
}
}

24.4 职责连的好处

     “这当中最关键的是当客户提交一个请求时, 请求时沿着职责连传递, 直至有一个ConcreteHandler 处理它[DP]
     “这样做的好处是不是说请求者不用管那个对象来处理, 反正该请求会被处理就对了?”
     “是的, 这就使得接受者和发送者都没有对方明确的信息, 且链中的对象自己也不知道链的结构. 结果是职责连可简化的对象相互连接, 他们仅需要保持一个只想其后继者的引用, 而不需要保存它所有候选者的引用[DP]. 也就大大降低了耦合度.”
     “我感觉由于是在客户端来定义链的结构, 也就是说, 我们可以随时的增加或修改处理一个请求的结构. 增强了给对象指派职责的灵活性.
     “是的, 这的确很灵活, 不过也要当心, 一个请求极有可能到了链的末端都得不到处理, 或者因为没有正确的配置而得不到处理. 这就很糟糕了, 需要事先全面考虑.”
     “哈, 这就跟现实中邮寄一封信, 因地址不对, 最终无法送达一样.”
     “是的, 就是这个意思. 就刚才的例子而言, 最重要的有两点, 一个是你需要给具体的管理者设置他具体的上司是哪一个, 也就是后继者. 另一点是你需要在每一个管理者处理请求时, 作出判断, 时刻以处理这个请求, 还是必须要 ‘推卸责任’, 转移给后继者去处理.”
     “哦, 我明白你的意思了, 其实这就是把我现在写的这个管理者类当中的那些分支, 分解到每一个具体的管理者类当中, 然后利用实现设置的后继者来实现请求处理的权限问题.”

24.5 加薪代码重构

     “是的, 所以我们先来改造这个管理者类, 此时它将成为抽象的父类, 其实它就是 Handler.”



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 管理者
public abstract class Manager {
protected String name;
// 管理者的上级
protected Manager superior;
public Manager(String name) {
this.name = name;
}
// 设置管理者的上级
public void setSuperior(Manager superior) {
this.superior = superior;
}
// 申请请求
abstract public void requestApplication(Request request);
}

     “经理类就可以去继承这个 ‘管理者’ 类, 只需重写 ‘申请请求’ 的方法就可以了.”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 经理
public class CommonManager extends Manager {
public CommonManager(String name) {
super(name);
}
@Override
public void requestApplication(Request request) {
if (request.getRequestType().equals("请假") && request.getNumber() < 2) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 被批准");
} else if (superior != null) {
superior.requestApplication(request);
}
}
}

     “‘总监’ 类同样继承 ‘管理这类’”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 总监
public class MajorDomo extends Manager {
public MajorDomo(String name) {
super(name);
}
@Override
public void requestApplication(Request request) {
if (request.getRequestType().equals("请假") && request.getNumber() <= 5) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 被批准");
} else if (superior != null) {
superior.requestApplication(request);
}
}
}

     “‘总经理’ 的权限就是全部都需要处理.”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 总经理
public class GeneralManager extends Manager {
public GeneralManager(String name) {
super(name);
}
@Override
public void requestApplication(Request request) {
if (request.getRequestType().equals("请假")) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 被批准");
} else if (request.getRequestType().equals("加薪") && request.getNumber() <= 500) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 被批准");
} else if (request.getRequestType().equals("加薪") && request.getNumber() > 500) {
System.out.println(name + ":" + request.getRequestContent() + " 数量" +
+request.getNumber() + " 再说吧");
}
}
}

     “由于我们把你原来的一个 ‘管理者’ 类改成一个抽象类和三个具体类, 此是类之间的灵活性就大大增加了, 如果我们要扩展新的管理者类别, 只需要增加子类就可以了. 当然, 还有一个关键就是, 客户端如何编写.”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public static void main(String[] args) {
Manager jinli = new CommonManager("金利");
Manager zongjian = new MajorDomo("宗剑");
Manager zhongjingli = new GeneralManager("钟精励");
// 设置上级, 可以根据需求动态设置
jinli.setSuperior(zongjian);
zongjian.setSuperior(zhongjingli);
// 客户端的申请都是由 '经理' 发起, 但实际谁来决策
// 由具体管理类来处理, 客户端不知道.
Request request = new Request();
request.setRequestType("请假");
request.setRequestContent("小菜请假");
request.setNumber(1);
jinli.requestApplication(request);
Request request2 = new Request();
request2.setRequestType("请假");
request2.setRequestContent("小菜请假");
request2.setNumber(4);
jinli.requestApplication(request2);
Request request3 = new Request();
request3.setRequestType("加薪");
request3.setRequestContent("小菜请求加薪");
request3.setNumber(500);
jinli.requestApplication(request3);
Request request4 = new Request();
request4.setRequestType("加薪");
request4.setRequestContent("小菜请求加薪");
request4.setNumber(1000);
jinli.requestApplication(request4);
}

     结果显示

1
2
3
4
金利:小菜请假 数量1 被批准
宗剑:小菜请假 数量4 被批准
钟精励:小菜请求加薪 数量500 被批准
钟精励:小菜请求加薪 数量1000 再说吧

     “恩, 这的确是很好的解决了原来大量的分支判断造成的难以维护, 灵活性差的问题.”

24.5 加薪成功

     正在此时, 小菜的手机响了起来.
     “喂,” 小菜说, “李经理, 你好.”
     “小菜呀, 是这样, 我刚才又去找总经理了, 向他也汇报了这一段时间你的工作情况, 你的积极表现和非常优秀的编程能力总经理也是非常肯定的. 所以他最终答应了你的申请, 给你加薪, 从下个月开始实施.” 小菜的经理在电话那边说道.
     “啊, 是吗! 真的谢谢您, 万分感谢. 回头我请您吃法. 恩, 好的, 好的, OK, 拜拜.” 小菜脸上笑开了花.
     “你这马屁精, 干嘛不跳槽?不是说要跳的吗?”
     “还跳什么槽呀. 我这经理人真的不错, 还专门去帮我找总经理谈, 太为下属着想了.”
     “哈, 事实上, 他是改变了职责连的结构, 跳过了总监直接找总经理处理这件事, 这也体现了职责连灵活性的一个方面.”
     “对的对的, 设计模式真的是无处不在呀.” 小菜笑着说, “走, 我请你吃炒面去.”
     “谁说是炒面, 我讲过的, 我要吃的是龙虾.”

    

~感谢捧场,您的支持将鼓励我继续创作~