“这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战”
一、什么是委派模式?
委派模式(Delegate Pattern)又叫委托模式,是一种面向对象的设计模式,允许对象组合实现与继承相同的代码重用。它的基本作用就是负责任务的调用和分配任务,是一种特殊的静态处理代理,可以理解为全权代理,但是代理模式注重过程,而委派模式注重结果。委派模式属于行为型模式,不属于GOF23种设计模式中。
二、委派模式涉及的角色
先来看一下类图:
从类图中我们可以看到,委派模式有三个参与角色:
- 抽象任务角色(Task):定义一个抽象接口,它有若干实现类。
- 委派者角色(Delegate):负责在各个具体角色实例之间做出决策,判断调用具体实现的方法。
- 具体任务角色(Concrete):真正执行任务的角色。
三、委派模式在业务场景中的应用
现实生活中也常有委派的场景发生,例如:老板(Boss)给项目经理(Leader)下达任务,项目经理会根据实际情况给每个员工派发工作任务,待员工把工作任务完成之后,再由项目经理汇报工作进度和结果给老板。
我们用代码来模式下这个业务场景,创建IEmployee员工接口:
public interface IEmployee {
void doing(String task);
}
创建员工EmployeeA类:
public class EmployeeA implements IEmployee {
protected String goodAt = "编程";
@Override
public void doing(String task) {
System.out.println("我是员工A,我擅长" + goodAt + ",现在开始做" + task + "工作");
}
}
创建员工EmployeeB类:
public class EmployeeB implements IEmployee {
protected String goodAt = "平面设计";
@Override
public void doing(String task) {
System.out.println("我是员工B,我擅长" + goodAt + ",现在开始做" + task + "工作");
}
}
创建项目经理Leader类:
public class Leader implements IEmployee {
private Map<String, IEmployee> employeeMap = new HashMap<>();
public Leader() {
employeeMap.put("爬虫", new EmployeeA());
employeeMap.put("插画", new EmployeeB());
}
@Override
public void doing(String task) {
if (!employeeMap.containsKey(task)) {
System.out.println("这个任务" + task + "超出我的能力范围");
return;
}
employeeMap.get(task).doing(task);
}
}
创建Boss类下达命令:
public class Boss {
public void command(String task, Leader leader) {
leader.doing(task);
}
}
测试代码:
public class Test {
public static void main(String[] args) {
Boss boss = new Boss();
Leader leader = new Leader();
boss.command("爬虫", leader);
boss.command("插画", leader);
boss.command("撩妹", leader);
}
}
运行结果如下:
通过上面的代码,生动的还原了项目经理分配工作的业务场景,也是委派模式的生动体现。
四、委派模式在源码中的体现
JDK中有一个典型的委派,众所周知JVM在加载类是用的双亲委派模式,这又是什么呢?一个类加载器在加载类时,先把这个请求委派给自己的父类加载器去执行,如果父类加载器还存在父类加载器,就继续向上委派,直到顶层的启动类加载器。如果父类加载器能够完成类加载,就成功返回,如果父类加载器无法完成加载,那么子类加载器才会尝试自己去加载。从定义中可以看到双亲委派加载模型,一个类加载器加载类时,首先不是自己加载,而是委派给父类加载器。下面我们来看看loadClass()方法的源码,此方法在ClassLoader中。在这个类里就定义了一个双亲,用于下面的类加载。
public abstract class ClassLoader {
....
private final ClassLoader parent;
....
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.deledate,并将这个对象作为一个参数传给parseBeanDefinitions(root,this.delegate)中,然后主要的解析的工作就是通过delegate作为主要角色完成的,可以看到下方代码:
DefaultBeanDefinitionDocumentReader类:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
其中最终能够走到Bean注册部分的是,会进入到parseDefaultElement(ele,delegate)中,然后针对不同的节点类型,针对Bean的节点进行真正的注册操作,而在这个过程中,delegate会对element进行parseBeanDefinitionElement,得到了一个BeanDefinitionHolder类型的对象,之后通过这个对象完成真正的注册到Factory的操作。
五、委派模式的优缺点
优点:
- 通过任务委派能够将一个大型的任务细化,然后通过统一管理这些子任务的完成情况实现任务的跟进,能够加快任务执行的效率。
缺点:
- 任务委派方式需要根据任务的复杂程度进行不同的改变,在任务比较复杂的情况下可能需要进行多重委派,容易造成絮乱。
六、友情链接
欢迎大家关注微信公众号(MarkZoe)互相学习、互相交流。