设计模式之委派模式

委派模式

委派模式

委派模式(Delegate Pattern)又叫委托模式,是一种面向对象的设计模式,允许对象组合实现与继承相同的代码重用。它的基本作用就是负责任务的调用和分配任务,是一种特殊的静态代理,可以理解为全权代理,但是代理模式注重过程,而委派模式注重结果。委派模式属于行为型模式,不属于GOF23种设计模式中。

委派模式的应用场景

委派模式在Spring中应用非常多,大家常用的DispatcherServlet其实就是用到了委派模式。

UML类图如下

image-20210424160857354

从类图中可以看到,委派模式有三个参与角色:

抽象任务角色(Task):定义一个抽象接口,它有若干实现类。

委派者角色(Delegate):负责在各个具体角色实例之间做出决策,并判断并调用具体实现的方法。

具体任务角色(Concrete):真正执行任务的角色。

现实生活中也常有委派的场景发生,例如:老板(Boss)给项目经理(Leader)下达任务,项目经理会根据实际情况给每个员工派发工作任务,待员工把工作任务完成之后,再由项目经理汇报工作进度和结果给老板。

通用代码

抽象任务角色(Task)

1
2
3
4
public interface Task
{
void doTask(String str);
}

具体任务角色(Concrete)

ConcreteA

1
2
3
4
5
6
7
8
public class ConcreteA implements Task
{
@Override
public void doTask(String str)
{
System.out.println("ConcreteA doTask " + str);
}
}

ConcreteB

1
2
3
4
5
6
7
8
public class ConcreteB implements Task
{
@Override
public void doTask(String str)
{
System.out.println("ConcreteB doTask " + str);
}
}

委派者角色(Delegate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Delegate implements Task
{

@Override
public void doTask(String str)
{

if("aTask".equals(str))
{
new ConcreteA().doTask(str);
}
else if("bTask".equals(str))
{
new ConcreteB().doTask(str);
}
}
}

测试代码

1
2
3
4
5
6
7
8
9
public class Main
{
public static void main(String[] args)
{
Delegate delegate = new Delegate();
delegate.doTask("aTask");
delegate.doTask("bTask");
}
}

输出

1
2
ConcreteA doTask aTask
ConcreteB doTask bTask

委派模式在源码中的体现

JDK中有一个典型的委派,众所周知JVM在加载类是用的双亲委派模型,一个类加载器在加载类时,先把这个请求委派给自己的父类加载器去执行,如果父类加载器还存在父类加载器,就继续向上委派,直到顶层的启动类加载器。如果父类加载器能够完成类加载,就成功返回,如果父类加载器无法完成加载,那么子加载器才会尝试自己去加载。从定义中可以看到双亲加载模型一个类加载器加载类时,首先不是自己加载,而是委派给父加载器。下面我们来看看loadClass()方法的源码,此方法在ClassLoader中。在这个类里就定义了一个双亲,用于下面的类加载。

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
41
public abstract class ClassLoader {
...
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}

if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);

// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
...
}

委派模式在Spring中的应用在Spring IoC中在调用doRegisterBeanDefinitions()方法时即BeanDefinition进行注册的过程中会设置BeanDefinitionParserDelegate类型的Delegate对象传给this.delegate,并将这个对象作为一个参数传给parseBeanDefinitions(root,this.delegate)中,然后主要的解析的工作就是通过delegate作为主要角色来完成的,可以看到下方代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){ 
//判断节点是否属于同一命名空间,是则执行后续的解析
if (delegate .isDefaultNamespace(root)){
Nodelist nl = root.getchildNodes();
for (int i = e; i < nl.getlength();i++){
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultelement(ele, delegate);
}
else{
//注解定义的Context的namespace进入到这个分支中
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

其中最终能够走到bean注册部分的是,会进入到parseDefaultElement(ele,delegate)中,然后针对不同的节点类型,针对bean的节点进行真正的注册操作,而在这个过程中,delegate会对element进行parseBeanDefinitionElement,得到了一个BeanDefinitionHolder类型的对象,之后通过这个对象完成真正的注册到Factory的操作。

SpringMVC的DispatcherServlet也是使用的委派模式。

委派模式的优缺点

优点:

通过任务委派能够将一个大型的任务细化,然后通过统一管理这些子任务的完成情况实现任务的跟进,能够加快任务执行的效率。

缺点:

任务委派方式需要根据任务的复杂程度进行不同的改变,在任务比较复杂的情况下可能需要进行多重委派,容易造成紊乱。

打赏

请我喝杯咖啡吧~

支付宝
微信