当前位置 博文首页 > 童话述说我的结局:委派模式

    童话述说我的结局:委派模式

    作者:童话述说我的结局 时间:2021-05-01 18:15

    一、定义

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

    委派模式有3个参与角色

    1. 抽象任务角色(ITask):定义一个抽象接口,它有若干实现类。
    2. 委派者角色(Delegate):负责在各个具体角色实例之间做出决策,判断并调用具体实现的方法。
    3. 具体任务角色(Concrete):真正执行任务的角色。

    二、委派模式的应用场景

     委派模式在业务场景中的例子很多:需要实现表现层和业务层之间的松耦合;需要编排多个服务之间的调用;需要封装一层服务查找和调用。前面说的都是业务场景,下面来说下生活场景中的例子,例如:大老板跟项目经理下了个任务,项目经理不可能自己亲自去做所有事吧,他肯定会把收到的任务进行分解,然后分给下面的员工下发任务,等员工把工作完成后,再把结果汇总向老板汇报

    //抽象任务角色
    public interface ITask {
        void doTask(String mission) throws IllegalAccessException, InstantiationException;
    }
    //具体任务角色 ConcreteA
    public class ConcreteA  implements ITask {
        @Override
        public void doTask(String mission) {
            System.out.println("我是员工A,我的工作是UI");
        }
    }
    //具体任务角色 ConcreteB
    public class ConcreteB implements ITask {
        @Override
        public void doTask(String mission) {
            System.out.println("我是员工B,我的工作是开发");
        }
    }
    //委派者角色 Delegate 经理
    public class Delegate implements ITask{
        private Map<String,Class> map=new HashMap<>();
    
        public Delegate(){
            map.put("UI",ConcreteA.class);
            map.put("开发",ConcreteB.class);
        }
        @Override
        public void doTask(String mission) throws IllegalAccessException, InstantiationException {
            if (!map.containsKey(mission)){
                System.out.println("没有这样的业务员");
                return;
            }
           ITask iTask= (ITask) map.get(mission).newInstance();
            iTask.doTask(mission);
    
        }
    }
    //老板
    public class Robam {
        public void command(String mission,ITask iTask) throws InstantiationException, IllegalAccessException {
            iTask.doTask(mission);
        }
    }
    public class Test {
        public static void main(String[] args) throws IllegalAccessException, InstantiationException {
            new Robam().command("UI",new Delegate());
        }
    }

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

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

    同样在Method类里常用的代理执行方法invoke()也存在类似的机制

    @CallerSensitive
        public Object invoke(Object obj, Object... args)
            throws IllegalAccessException, IllegalArgumentException,
               InvocationTargetException
        {
            if (!override) {
                if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                    Class<?> caller = Reflection.getCallerClass();
                    checkAccess(caller, clazz, obj, modifiers);
                }
            }
            
            MethodAccessor ma = methodAccessor;             // read volatile
            if (ma == null) {
                ma = acquireMethodAccessor();
            }
            //MethodAccessor没有做任何事情只是拿到了ma的返回结果而已
            return ma.invoke(obj, args);
        }

    IOC中对象实例化委派模式

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

     

    /**
     * Parse the elements at the root level in the document:
     * "import", "alias", "bean".
     * @param root the DOM root element of the document
     */
     
    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)) {
     
                   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

    DispatcherServlet 虽然没带delegate,但也是委派模式的一种实现。

    前端请求都统一走到DispatcherServlet 的doService()方法中,然后在doService()方法中调用doDispatch()方法,在doDispatch()方法中,会获取业务处理的handler,执行handle()方法处理请求。

    doDispatch()方法核心源码截图
     

     

     看过源码的人从上面逻辑可以知道用于HTTP请求处理程序/控制器的中央调度程序,针对通过WEB UI输入的url请求,委派给DispatcherServlet处理,从委派者的角度来看,关注结果即可

     四、总结

    优点:

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

    缺点:

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

    委派模式与代理模式异同

    代理模式是由代理来帮你完成一些工作,而这里的委派模式,是由委派对象来帮你完成一些工作,字面上来看,好像并没有什么差别。首先,我们代理可以增强我们的代理目标类,而委派模式,像上面的例子,老板要做一件事只用跟经理说下就行,接下来的所有的事情,都交给经理去处理即可了,自己完全不必实际去参与到行动中。

     

    git源码:https://github.com/ljx958720/design_patterns.git

    bk
    下一篇:没有了