行为型模式
目录
2.2.2 SpringMVC中,类DispatcherServlet
1、委派模式
委派模式不属于GOF23种设计模式,但是在Spring中委派模式确实用的比较多的一种模式。本文将此模式归类为行为型模式。主要角色有三种: 抽象任务角色, 委派者角色, 具体任务角色。有前辈将其总结为代理模式和策略模式的组合。
意图:定义抽象接口的一个实现类, 他负责判断和调用哪个实现类的实例。
委派模式对外隐藏了具体实现, 仅将委派者角色暴露给外部, 如Spring的DispatcherServlet.
1.1 委派模式UML类图
1.2 日常生活中看委派模式
如公司老板想举办年会,这个时候,需要很多人员参与配合,需要有人写PPT,需要有人布置会场,需要有人采购,还需要有人宣传等。这个时候,老板本人并不知道公司有哪些同事擅长做这些事情,于是,老板就将这个艰巨的任务交给了HR,然后让HR给安排合适的任选进行工作的进行。这就是一个比较简单易懂的委派模式的表现。
1.3 Java代码实现
1.3.1 定义抽象任务角色接口
/**
* 抽象任务角色
*/
public interface Task {
void doTask();
}
1.3.2 具体任务角色, 实现上面的接口, 这里定义两个实现类
/**
* 具体实现类A
*/
public class ConcreteTaskA implements Task {
public void doTask() {
System.out.println("执行 , 由A实现");
}
}
/**
* 具体实现类B
*/
public class ConcreteTaskB implements Task {
public void doTask() {
System.out.println("执行 , 由B实现");
}
}
1.3.3 委派角色, 是整个模式的核心角色, 下面代码中我们使用随机数来判断应该实例化哪个具体实现类
import java.util.Random;
/**
* 代理角色
*/
public class TaskDelegate implements Task{
public void doTask() {
System.out.println("代理执行开始....");
Task task = null;
if (new Random().nextBoolean()){
task = new ConcreteTaskA();
task.doTask();
}else{
task = new ConcreteTaskB();
task.doTask();
}
System.out.println("代理执行完毕....");
}
}
1.3.4 调用
public class TaskTest {
public static void main(String[] args) {
new TaskDelegate().doTask();
}
}
2、委派模式在源码中的应用
2.1 JDK源码中委派模式体现
JDK中有一个典型的委派,JVM在加载类时是用的双亲委派模型。
一个类加载器在加载类时,先把这个请求委派给自己的父类加载器去执行,如果父类加载器还存在父类加载器,就继续向上委派,直到顶层的启动类加载器。
如果父类加载器能过完成类加载,就成功返回。如果父类加载器无法完成加载,那么子加载器才会尝试自己去加载。从定义中可看出双亲加载模型一个类加载器加载类时,首先不是自己加载,而是委派给父加载器。
在下方的代码示例中,loadClass()方法,定义在ClassLoader中,在这个类中就定义了一个双亲,用于下面的类加载。
public abstract class ClassLoader {
//......中间代码省略
// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
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;
}
}
}
2.2 Spring源码中委派模式体现
2.2.1 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的操作
2.2.2 SpringMVC中,类DispatcherServlet
当然了,我们熟知的DispatcherServlet 虽然没带delegate,但也是委派模式的一种实现。
前端请求都统一走到DispatcherServlet 的doService()方法中,然后在doService()方法中调用doDispatch()方法,在doDispatch()方法中,会获取业务处理的handler,执行handle()方法处理请求。
doDispatch()方法核心源码截图
就是:用于HTTP请求处理程序/控制器的中央调度程序,针对通过WEB UI输入的url请求,委派给DispatcherServlet处理,从委派者的角度来看,关注结果即可
3、委派模式优缺点
3.1 优点
通过任务委派能够将一个大型的任务细化,然后通过统一管理这些子任务的完成情况实现任务的跟进,能够加快任务执行的效率。
3.2 缺点
任务委派方式需要根据任务的复杂程度进行不同的改变,在任务比较复杂的情况下可能需要进行多重委派,容易造成紊乱。
3.3 应用场景
- 当你要实现表现层和业务层之间的松耦合的时候。
- 当你想要编排多个服务之间的调用的时候。
- 当你想要再封装一层服务查找和调用时候
4、委派模式与代理模式异同
我们知道,代理模式是由代理来帮你完成一些工作,而这里的委派模式,是由委派对象来帮你完成一些工作,字面上来看,好像并没有什么差别。那么,既然分为两种不同 的设计模式,那么它们俩之间肯定是有差别的。
首先,我们知道代理可以增强我们的代理目标类,比如,王婆帮西门庆安排了一场约会,这里的王婆,就是西门庆的代理了。但是,具体的参与约会的对象,依然是西门庆,而并不是王婆。所以,代理模式中,被代理类还是需要进行实际去参与行动。
而委派模式,像上面的例子,boss想举办一场豪华的年会,他只需要告诉HR小姐姐即可,接下来的所有的事情,都交给小姐姐去处理即可了,自己完全不必实际去参与到行动中。
这就是代理模式和委派模式的区别所在。
参考文章: