You think the delegation model is mysterious, but you use it every day

Posted by Amit Rathi on Tue, 09 Nov 2021 11:53:54 +0100

This article is excerpted from "design patterns should be learned this way"

1. Use delegation mode to simulate task allocation scenarios

We use code to simulate the business scenario where the boss assigns tasks to employees. First, create the IEmployee employee interface.

public interface IEmployee {
    void doing(String task);
}

Create the employee EmployeeA class.

public class EmployeeA implements IEmployee {
    protected String goodAt = "programming";
    public void doing(String task) {
        System.out.println("I'm an employee A,I'm good at" + goodAt + ",Start now" +task + "work");
    }
}

Create the employee EmployeeB class.

public class EmployeeB implements IEmployee {
    protected String goodAt = "Graphic Artist Designer";
    public void doing(String task) {
        System.out.println("I'm an employee B,I'm good at" + goodAt + ",Start now" +task + "work");
    }
}

Create the project manager Leader class.

public class Leader implements IEmployee {

    private Map<String,IEmployee> employee = new HashMap<String,IEmployee>();

    public Leader(){
        employee.put("Reptile",new EmployeeA());
        employee.put("Poster",new EmployeeB());
    }

    public void doing(String task) {
        if(!employee.containsKey(task)){
            System.out.println("This task" +task + "Beyond my ability");
            return;
        }
        employee.get(task).doing(task);
    }
}

Then create the Boss class and issue the command.

public class Boss {
    public void command(String task,Leader leader){
        leader.doing(task);
    }
}

Finally, write the client test code.

public class Test {
    public static void main(String[] args) {
        new Boss().command("Poster",new Leader());
        new Boss().command("Reptile",new Leader());
        new Boss().command("Selling mobile phones",new Leader());
    }
}

Through the above code, we vividly restore the business scenario of the boss assigning tasks, which is also a vivid embodiment of the delegation mode. The class diagram is shown in the figure below.

2 Application of delegation mode in JDK source code

There is a typical delegation in JDK. As we all know, the JVM uses the two parent delegation model when loading classes. What is this? When a class loader loads a class, it first delegates the request to its parent class loader for execution. If the parent class loader still exists, continue to delegate upward until the top-level startup class loader; If the parent class loader can complete class loading, it returns successfully; If the parent loader cannot finish loading, the child loader attempts to load itself. As can be seen from the definition, when a class loader loaded by the parent delegation loads a class, it is not loaded by itself, but delegated to the parent class loader. Let's look at the source code of the loadClass() method, which is in ClassLoader. In this class, a parent is defined for the following class loading.

public abstract class ClassLoader {
    ...
    private final ClassLoader parent;
    ...
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
              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) {
                }

                if (c == null) {
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
    ...
}

Similarly, in the Method class, the commonly used proxy execution Method invoke() has a similar mechanism. The code is as follows.

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();
        }
        return ma.invoke(obj, args);
}

After reading the code, I believe the partners have made clear the difference between delegation mode and agent mode.

3 application of delegation mode in Spring source code

Let's look at the application of delegation mode in Spring. The DefaultBeanDefinitionDocumentReader class in the Spring IoC module will set the delegate object of BeanDefinitionParserDelegate type to this.delegate when calling the doRegisterBeanDefinitions() method, that is, during the registration of BeanDefinition, And pass this object as a parameter into parsebean definitions (root, this. Delegate). The main parsing work is completed by delegate as the main role. The code is as follows.

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   //Judge whether the nodes belong to the same namespace. If so, perform subsequent resolution
   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 {
               //The nameSpace of the Context defined by the annotation enters this branch
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

The parseDefaultElement(ele, delegate) method in the above code is mainly used to complete the Bean registration operation for different node types. In this process, delegate will call the parseBeanDefinitionElement() method of element to obtain an object of BeanDefinitionHolder type, and then complete the registration through this object. Let's restore how the dispatcher servlet of Spring MVC implements the delegation mode. Create a business class MemberController.

/**
 * Created by Tom.
 */
public class MemberController {

    public void getMemberById(String mid){

    }

}

Create the OrderController class.

/**
 * Created by Tom.
 */
public class OrderController {

    public void getOrderById(String mid){

    }

}

Create the SystemController class.

/**
 * Created by Tom.
 */
public class SystemController {

    public void logout(){

    }
}

Create the DispatcherServlet class.

public class DispatcherServlet extends HttpServlet {

    private Map<String,Method> handlerMapping = new HashMap<String,Method>();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatch(req,resp);
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
        String url = req.getRequestURI();
        Method method = handlerMapping.get(url);
       //The code for the reflection calling method is omitted here
           ...

    }

    @Override
    public void init() throws ServletException {
        try {
            handlerMapping.put("/web/getMemeberById.json", MemberController.class.getMethod("getMemberById", new Class[]{String.class}));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

Configure the web.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
   xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
   version="2.4">
   <display-name>Web Application</display-name>


   <servlet>
      <servlet-name>delegateServlet</servlet-name>
      <servlet-class>com.tom.pattern.delegate.mvc.DispatcherServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>

   <servlet-mapping>
      <servlet-name>delegateServlet</servlet-name>
      <url-pattern>/*</url-pattern>
   </servlet-mapping>

</web-app>

In this way, a complete delegation pattern is implemented. Of course, there are many cases of using delegation mode in Spring, which can be recognized by naming. In the Spring source code, all that ends with Delegate implement the delegation pattern. For example, BeanDefinitionParserDelegate delegates different logic according to different types to resolve BeanDefinition.

[recommendation] Tom bomb architecture: collecting this article is equivalent to collecting a book on "design patterns"

This article is the original of "Tom bomb architecture". Please indicate the source for reprint. Technology lies in sharing, I share my happiness!
If this article is helpful to you, you are welcome to pay attention and praise; If you have any suggestions, you can also leave comments or private letters. Your support is the driving force for me to adhere to my creation. Focus on WeChat official account Tom structure, get more dry cargo!

Topics: Java Design Pattern architecture