职责链模式

“从小,在家中总是处在食物链顶端。”

被处理类

试想有这样一个问题,父亲去超市买了两个水果,分别是苹果和橘子,为了不浪费,这些水果需要被由小孩、母亲和父亲组成的家庭吃掉。先来构造水果类:

1
2
3
interface Fruit06 {
String getColor();
}

继续构造苹果和橘子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Apple06 implements Fruit06 {
private String color = "red";
@Override
public String getColor() {
return color;
}
}
class Orange06 implements Fruit06 {
private String color = "orange";
@Override
public String getColor() {
return color;
}
}

将水果包装进一个Thing类方便处理:

1
2
3
4
5
6
7
8
9
10
class Thing {
private Fruit06 f;
public Thing(Fruit06 f) {
this.f = f;
}
public Fruit06 getF() {
return f;
}
}

抽象的“职责”类

水果买到家,先看孩子吃不吃,孩子不吃再看妈妈吃不吃,妈妈如果也不吃,最后由爸爸无条件吃光。

构造一个抽象类:

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
abstract class SendToMember {
private String name;
private SendToMember next;
public SendToMember(String name) {
this.name = name;
}
public SendToMember setNext(SendToMember next) {
this.next = next;
return next;
}
public final void sendToMember(Thing thing) {
if (resolve(thing)) {
done(thing);
} else if (next != null) {
next.sendToMember(thing);
} else {
fail(thing);
}
}
@Override
public String toString() {
return "[" + name + "]";
}
protected abstract boolean resolve(Thing thing);
protected void done(Thing thing) {
System.out.println("被" + this + "吃掉");
}
protected void fail(Thing thing) {
System.out.println("不能被吃掉");
}
}

可以把一条职责链当成一条链表,链表上的每一个node都是拥有不同“职责”的节点,而这些节点都继承于抽象类SendToMember。在SendToMember中,resolve方法交给继承类去实现,也恰恰是各个节点的resolve方法不同,才赋予它们不同的“职责”。

具体的“职责”类

在本例中,会出现三种“职责”,一种是厌食(不吃),一种是偏食(选择性的吃)以及通食(什么都吃)。

厌食:

1
2
3
4
5
6
7
8
9
10
11
class NoEat extends SendToMember {
public NoEat(String name) {
super(name);
}
@Override
protected boolean resolve(Thing thing) {
return false;
}
}

偏食:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class LimitEat extends SendToMember {
private String limit;
public LimitEat(String name, String limit) {
super(name);
this.limit = limit;
}
@Override
protected boolean resolve(Thing thing) {
if (thing.getF().getColor() == limit) {
return true;
} else {
return false;
}
}
}

通食:

1
2
3
4
5
6
7
8
9
10
class AllEat extends SendToMember {
public AllEat(String name) {
super(name);
}
@Override
protected boolean resolve(Thing thing) {
return true;
}
}

上面的代码中厌食的resolve方法都反回false,偏食有选择的返回true,而通食无论遇到哪种水果都是返回true。

测试用例

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
SendToMember child = new NoEat("Child");
SendToMember mother = new LimitEat("Mother", "red");
SendToMember father = new AllEat("Father");
child.setNext(mother).setNext(father);
child.sendToMember(new Thing(new Orange06()));
child.sendToMember(new Thing(new Apple06()));
}

小孩爱吃糖不爱吃水果,什么水果都无法吸引他;母亲喜欢吃红色的水果,觉得吃啥补啥,越吃脸越红彤彤,美美美;父亲节约无忌口,不吃觉得浪费,做家庭的“光盘”捍卫者。

设计模式的原则

迪米特法则、开闭原则等。水果类作为对象,并不需要关心到底是谁吃掉了它们,而且在该设计模式下程序很容易得到扩展,比如家里来了客人,可以动态改变职责链。

参考资料

图解设计模式