31.1 代理模式VS装饰模式

31.1 代理模式VS装饰模式

对于两个模式,首先要说的是,装饰模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点则是代理模式着重对代理过程的控制,而装饰模式则是对类的功能进行加强或减弱,它着重类的功能变化,我们举例来说明它们的区别。

31.1.1 代理模式

一个著名的短跑运动员有自己的代理人。如果你很仰慕他,你找运动员说“你跑个我看看”,运动员肯定不搭理你,不过你找到他的代理人就不一样了,你可能和代理人比较熟, 可以称兄道弟,这个忙代理人还是可以帮的,于是代理人同意让你欣赏运动员的练习赛,这对你来说已经是莫大的荣耀了。我们来看类图,如图31-1所示。

image-20210930161729112

图31-1 运动员跑步
这是一个套用代理模式的简单应用,非常简单!一个对象,然后再是自己的代理。我们先来看一下代码,先看抽象主题类,如代码清单31-1所示。

代码清单31-1 抽象运动员

1
2
3
4
public interface IRunner {
//运动员的主要工作就是跑步
public void run();
}

一个具体的短跑运动员跑步是很潇洒的,如代码清单31-2所示。

代码清单31-2 运动员跑步

1
2
3
4
5
public class Runner implements IRunner {
public void run() {
System.out.println("运动员跑步:动作很潇洒");
}
}

看看现在的明星运动员,一般都有自己的代理人,要么是专职的,要么就是自己的教练兼职,那我们来看看代理人的职责,如代码清单31-3所示。

代码清单31-3 代理人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class RunnerAgent implements IRunner {
private IRunner runner;
public RunnerAgent(IRunner _runner){
this.runner = _runner;
}
//代理人是不会跑的
public void run() {
Random rand = new Random();
if(rand.nextBoolean()){
System.out.println("代理人同意安排运动员跑步");
runner.run();
}
else{
System.out.println("代理人心情不好,不安排运动员跑步");
}
}
}

我们只是定义了一个代理人,并没有明确定义是哪一个运动员的代理,需要在运行时指定被代理者,而且我们还在代理人的run方法中做了判断,想让被代理人跑步就跑步,不乐意就拒绝,对于主题类的行为是否可以发生,代理类有绝对的控制权。我们编写一个场景类来模拟这种情况,如代码清单31-4所示。

代码清单31-4 场景类

1
2
3
4
5
6
7
8
9
10
11
public class Client {
public static void main(String[] args) {
//定义一个短跑运动员
IRunner liu = new Runner();
//定义liu的代理人
IRunner agent = new RunnerAgent(liu);
//要求运动员跑步
System.out.println("====客人找到运动员的代理要求其去跑步===");
agent.run();
}
}

由于我们使用了随机数产生模拟结果,因此运行结果有两种可能情况,第一种情况如下所示:

1
2
3
4
5
6
====客人找到运动员的代理要求其去跑步=== 
代理人同意安排运动员跑步
运动员跑步:动作很潇洒
运行结果的第二种情况如下所示:
====客人找到运动员的代理要求其去跑步===
代理人心情不好,不安排运动员跑步

不管是哪种情况,我们都证实了代理的一个功能:在不改变接口的前提下,对过程进行控制。在我们例子中,运动员要不要跑步是由代理人决定的,代理人说跑步就跑步,说不跑就不跑,它有绝对判断权。

31.1.2 装饰模式

如果使用装饰模式,我们该怎么实现这个过程呢?装饰模式是对类功能的加强,怎么加强呢?增强跑步速度!在屁股后面安装一个喷气动力装置,类似火箭的喷气装置,那速度变得很快,《蜘蛛侠》中的那个反面角色不就是这样的吗?好,我们来看类图,如图31-2所示。

image-20210930162043387

图31-2 增强运动员的功能
很惊讶?这个代理模式完全一样的类图?是的,完全一样!不过其实现的意图却不同, 我们先来看代码,IRunner和Runner与代理模式相同,详见代码清单31-1和代码清单31-2所示,在此不再赘述。我们来看装饰类RunnerWithJet,如代码清单31-5所示。

代码清单31-5 装饰类

1
2
3
4
5
6
7
8
9
10
public class RunnerWithJet implements IRunner {
private IRunner runner;
public RunnerWithJet(IRunner _runner){
this.runner = _runner;
}
public void run() {
System.out.println("加快运动员的速度:为运动员增加喷气装置");
runner.run();
}
}

这和代理模式中的代理类也是非常相似的,只是装饰类对类的行为没有决定权,只有增强作用,也就是说它不决定被代理的方法是否执行,它只是再次增加被代理的功能。我们来看场景类,如代码清单31-6所示。

代码清单31-6 场景类

1
2
3
4
5
6
7
8
9
10
11
public class Client {
public static void main(String[] args) {
//定义运动员
IRunner liu = new Runner();
//对其功能加强
liu = new RunnerWithJet(liu);
//看看它的跑步情况如何
System.out.println("===增强后的运动员的功能===");
liu.run();
}
}

运行结果如下所示:

1
2
3
===增强后的运动员的功能=== 
加快运动员的速度:为运动员增加喷气装置
运动员跑步:动作很潇洒

注意思考一下我们的程序,我们通过增加了一个装饰类,就完成了对原有类的功能增加,由一个普通的短跑运动员变成了带有喷气装置的超人运动员,其速度岂是普通人能相比的?!

31.1.3 最佳实践