设计模式-委派模式学习之旅

1,408 阅读4分钟

“这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战

一、什么是委派模式?

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

二、委派模式涉及的角色

先来看一下类图:

image.png

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

  1. 抽象任务角色(Task):定义一个抽象接口,它有若干实现类。
  2. 委派者角色(Delegate):负责在各个具体角色实例之间做出决策,判断调用具体实现的方法。
  3. 具体任务角色(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);
    }
}

运行结果如下:

image.png

通过上面的代码,生动的还原了项目经理分配工作的业务场景,也是委派模式的生动体现。

四、委派模式在源码中的体现

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)互相学习、互相交流。