38.3 雇工模式

38.3 雇工模式

38.3.1 雇工合作

我是一个富豪(当然只是想象中的),家里有很多佣人,家务活基本上不用我动手,我只要动动口就可以了,在这里每个人都有不同分工,我可以指挥厨师把厨房弄干净,这是他的地盘;我可以指挥园丁把花园收拾干净、漂亮,这是他应该做的;我还可以让裁缝把我的衣服收拾干净。注意看,我这里列举出的三个对象(厨师、园丁、裁缝)都具有相同的功能:清洁。从另一方面说,厨房、花园、衣服都具有被清洁的特性,我们从这一例子入手, 编写代码如代码清单38-32所示。

代码清单38-32 三个对象的被清洁特质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//可以被清洁的对象 
public interface Cleanable {
//被清洁
public void celaned();
}
//花园
class Garden implements Cleanable{
public void celaned(){
System.out.println(“花园被清洁干净”);
}
}
//厨房
class Kitchen implements Cleanable{
public void celaned(){
System.out.println(“厨房被清洁干净”);
}
}
//衣服
class Cloth implements Cleanable{
public void celaned(){
System.out.println(“衣服被清洁干净”);
}
}

三个对象(厨房、花园、衣服)的共同特征抽取出来,同时也需要把厨师、裁缝、园丁的共同特征也抽象出来。从我这个主人的角度看来,他们三者都是清洁者,只是输入的对象不同而已,如代码清单38-33所示。

代码清单38-33 抽象的清洁者

1
2
3
4
5
6
public class Cleaner {
//清洁
public void clean(Cleanable clean){
clean.celaned();
}
}

三个对象(厨房、花园、衣服)的共同特征抽取出来,同时也需要把厨师、裁缝、园丁的共同特征也抽象出来。从我这个主人的角度看来,他们三者都是清洁者,只是输入的对象不同而已,如代码清单38-33所示。

代码清单38-33 抽象的清洁者

1
2
3
4
5
6
public class Cleaner {
//清洁
public void clean(Cleanable clean){
clean.celaned();
}
}

非常简单,就这么一个清洁者就可以厨师、园丁、裁缝。我们再编写一个场景类,描述一下发生了什么事,如代码清单38-34所示。

代码清单38-34 场景类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Client {
public static void main(String[] args) {
//厨师清洁厨房
Cleaner cookie = new Cleaner();
cookie.clean(new Kitchen());
//园丁清洁花园
Cleaner gardener = new Cleaner();
gardener.clean(new Garden());
//裁缝清洁衣服
Cleaner tailer = new Cleaner();
tailer.clean(new Cloth());
}
}

场景写完了,运行一下,就可以看到厨师打扫了厨房,园丁清洁了花园,裁缝清洁了衣服。代码很简单,但是诸位有没有发觉这和我们通常的分析是不同的。通常的做法是:既然厨师、园丁、裁缝都具有清洁的功能,那就定义一个接口描述三者的清洁功能,然后再定义三个类,分别代表厨师、园丁、裁缝实现这个接口。这是一种常用的解决办法,可以解决该问题,但今天我们从另外一个侧面进行分析,引出一个新的模式:雇工模式。

38.3.2 雇工模式的意图

雇工模式也叫做仆人模式(Servant Design Pattern),其意图是:

雇工模式是行为模式的一种,它为一组类提供通用的功能,而不需要类实现这些功能, 它是命令模式的一种扩展[^1]。

看看我们的例子,厨师、裁缝、园丁是一组类,都具有清洁的能力,但是我们却没实现,而是采用一种更优雅的方式来实现,这就是雇工模式。雇工模式的类图如图38-7所示。

image-20211001235152512

图38-7 雇工模式通用类图
在类图中,IServiced是用于定义“一组类”所具有的功能,其示例代码如代码清单38-35所示。

代码清单38-35 通用功能

1
2
3
4
public interface IServiced {
//具有的特质或功能
public void serviced();
}

针对不同的服务对象具备不同的服务内容,也就是具体的功能实现IServiced接口即可, 示例代码如代码清单38-36所示。

代码清单38-36 定义具体功能

1
2
3
4
5
6
7
8
public class Serviced1 implements IServiced {
public void serviced(){
}
}
public class Serviced2 implements IServiced{
public void serviced(){
}
}

功能定义完毕后,我们需要由一个雇工来执行这些功能。简单地说,就是需要有一个执行者,可以把一组功能聚集起来,示例代码如代码清单38-37所示。

代码清单38-37 雇工类

1
2
3
4
5
6
public class Servant {
//服务内容
public void service(IServiced serviceFuture){
serviceFuture.serviced();
}
}

在整个雇工模式中,所有具有IServiced功能的类可以实现该接口,然后由雇工类Servant 进行集合,完成一组类不用实现通用功能而具有相应职能的目的。

38.3.3 最佳实践

在日常的开发过程中,我们可能已经接触过雇工模式,只是我们没有把它抽取出来,也没有汇编成册。或许大家已经看出这与命令模式非常相似,读者可以回顾第15章,会发现雇工模式是命令模式的一种简化,但它更符合我们实际的需要,更容易引入开发场景中。

[^1]: 原文是A behavioral pattern used to offer some functionality to a group of classes without defining that functionality in each of them。