委派模式


一、定義

委派模式又叫委托模式,是一種面向對象的設計模式,允許對象組合實現與繼承相同的代碼重用。它的基本作用就是負責任務的調用和分配任務,是一種特殊的靜態代理,可以理解為全權代理,但是代理模式注重過程,而委派模式注重結果。委派模式屬於行為型模式,不屬於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://gitee.com/TongHuaShuShuoWoDeJieJu/design_pattern.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM