代码地址:https://github.com/ShouZhiDuan/NB/tree/main/study-list/java-basis/src/main/java/com/nb/java/designmode/delegate/pattern1
Delegate Pattern
委派模式(Delegate Pattern)又叫做委托模式,是一种面向对象的设计模式,允许对象组合实现与继承相同的代码重用。它的基本作用就是负责任务的调度和分发,也可以理解为是一种特殊的静态代理,但是代理模式注重的是代理的过程,而委派模式注重的是结果。委派模式属于行为模式,不属于(GOF)23种设计模式。
注意:委派模式属于行为模式,不属于(GOF)23种设计模式
一、应用场景
委派模式在Spring框架中应用非常多,比如说常见的DispatcherServlet其实就是用到了委派模式。
其中简单的场景大家可以想一下,我们平时开发会写很多的Controller,而且每个Controller中会写很多的方法这个方法就是大家常说的接口(带有@PostMapping、@GetMapping注解标注了不同的接口路劲
)。那SpringMVC是如何通过浏览器传过来的路径来分发或者决定调用哪个Controller的哪个方法呢?这块的逻辑就是可以简单理解为DispatcherServlet相当于一个分发器来委派到某个具体的Controller来执行,然后响应给浏览器。
二、实际应用(案例一)
1.业务场景
简单模拟实现DispatcherServlet是如何根据URL路径
分发到Controller的具体方法的。
2.代码实现
- InvokeMethod
- MyController
- MyDispatcherServlet
- TestMain
InvokeMethod
package com.nb.java.designmode.delegate.pattern1;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.lang.reflect.Method;
/**
* @Version 1.0
* @program: NB
* @ClassName InvokeMethod
* @author: duanshouzhi
* @create: 2023-07-08 16:56
* @description: controller里每个方法执行上下文
**/
@Data
@AllArgsConstructor
public class InvokeMethod {
/**
* Controller的实例,反射的时候会用到
*/
private Object target;
/**
* Controller每个方法对应的method实例
*/
private Method method;
/**
* Controller每个方法对应的实参列表
*/
private Object[] params;
}
MyController
package com.nb.java.designmode.delegate.pattern1;
import lombok.Getter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.bind.annotation.PostMapping;
/**
* @Version 1.0
* @program: NB
* @ClassName MyController
* @author: duanshouzhi
* @create: 2023-07-08 16:58
* @description:
**/
public class MyController {
@GetMapping(name = "/test1")
public String test1(){
System.err.println("======调用MyController.test1()方法======");
return "OK";
}
@PostMapping(name = "/test2")
public String test2(String msg){
System.err.println("======调用MyController.test2("+msg+")方法======");
return "OK";
}
}
MyDispatcherServlet
package com.nb.java.designmode.delegate.pattern1;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
/**
* @Version 1.0
* @program: NB
* @ClassName MyDispatcherServlet
* @author: duanshouzhi
* @create: 2023-07-08 17:02
* @description:
**/
public class MyDispatcherServlet {
private final static HashMap<String, InvokeMethod> INVOKE_METHODS;
static {
INVOKE_METHODS = new HashMap<>();
//解析MyController中所有的方法信息:注解数据、方法本身
MyController target = new MyController();
Method[] declaredMethods = MyController.class.getDeclaredMethods();
if(declaredMethods != null || declaredMethods.length > 0){
for (Method declaredMethod : declaredMethods) {
boolean getMappingPresent = declaredMethod.isAnnotationPresent(GetMapping.class);
boolean postMappingPresent = declaredMethod.isAnnotationPresent(PostMapping.class);
if(getMappingPresent){
System.out.println("======Get注解处理======");
GetMapping annotation = declaredMethod.getAnnotation(GetMapping.class);
String url = annotation.name();
System.out.println("当前方法的注解URL:" + url);
InvokeMethod invokeMethod = new InvokeMethod(target, declaredMethod, null);
INVOKE_METHODS.put(url,invokeMethod);
}
if(postMappingPresent){
System.out.println("======Get注解处理======");
PostMapping annotation = declaredMethod.getAnnotation(PostMapping.class);
String url = annotation.name();
System.out.println("当前方法的注解URL:" + url);
InvokeMethod invokeMethod = new InvokeMethod(target, declaredMethod, new Object[]{"测试参数"});
INVOKE_METHODS.put(url,invokeMethod);
}
}
}
}
/**
* 委派分发
* @param url
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public void handRequestByUrl(String url) throws InvocationTargetException, IllegalAccessException {
InvokeMethod invokeMethod = INVOKE_METHODS.get(url);
if(invokeMethod == null){
System.out.println("===404===");
}else {
Object invoke = invokeMethod.getMethod().invoke(invokeMethod.getTarget(), invokeMethod.getParams());
}
}
}
TestMain
package com.nb.java.designmode.delegate.pattern1;
import java.lang.reflect.InvocationTargetException;
/**
* @Version 1.0
* @program: NB
* @ClassName TestMain
* @author: duanshouzhi
* @create: 2023-07-08 17:22
* @description: 测试模拟浏览器请求
**/
public class TestMain {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
MyDispatcherServlet dispatcherServlet = new MyDispatcherServlet();
// 1、模拟请求 /test1
dispatcherServlet.handRequestByUrl("/test1");
// 2、模拟请求 /test2
dispatcherServlet.handRequestByUrl("/test2");
}
}
3.运行结果
三、实际应用(案例二)
1.业务场景
老师委派班长去干活^_^
假设学校元旦晚会老师(Teaher
)希望班长(Monitor
)告诉班里学习委员、文艺委员、劳动委员同学(Student
)各自上报一个节目。
简单类图:
2.代码实现
- Student
- Monitor
- CommissaryInChargeOfPhysicalLabor
- IiteratureAndArtCommitteeMember
- StudyCommitteeMember
- Teacher
- TestMain
Student
package com.nb.java.designmode.delegate.pattern2;
/**
* @Version 1.0
* @program: NB
* @ClassName Enjoy
* @author: duanshouzhi
* @create: 2023-07-08 22:41
* @description:
**/
public interface Student {
/**
* 报名节目
*/
void registrationProgram(String role);
}
Monitor
package com.nb.java.designmode.delegate.pattern2;
import java.util.HashMap;
/**
* @Version 1.0
* @program: NB
* @ClassName Monitor
* @author: duanshouzhi
* @create: 2023-07-08 22:55
* @description: 班长
**/
public class Monitor implements Student{
private final static HashMap<String,Student> STUDENT_HASH_MAP;
static {
// power of 2 for hashmap
STUDENT_HASH_MAP = new HashMap<>(4);
STUDENT_HASH_MAP.put("学习委员",new StudyCommitteeMember());
STUDENT_HASH_MAP.put("劳动委员",new CommissaryInChargeOfPhysicalLabor());
STUDENT_HASH_MAP.put("文艺委员",new IiteratureAndArtCommitteeMember());
}
@Override
public void registrationProgram(String role) {
Student student = STUDENT_HASH_MAP.get(role);
student.registrationProgram(role);
}
}
CommissaryInChargeOfPhysicalLabor
package com.nb.java.designmode.delegate.pattern2;
/**
* @Version 1.0
* @program: NB
* @ClassName CommissaryInChargeOfPhysicalLabor
* @author: duanshouzhi
* @create: 2023-07-08 22:52
* @description: 劳动委员
**/
public class CommissaryInChargeOfPhysicalLabor implements Student{
@Override
public void registrationProgram(String role) {
System.out.println("我是劳动委员:我要报名表演打扫卫生");
}
}
IiteratureAndArtCommitteeMember
package com.nb.java.designmode.delegate.pattern2;
/**
* @Version 1.0
* @program: NB
* @ClassName IiteratureAndArtCommitteeMember
* @author: duanshouzhi
* @create: 2023-07-08 22:53
* @description: 文艺委员
**/
public class IiteratureAndArtCommitteeMember implements Student{
@Override
public void registrationProgram(String role) {
System.out.println("我是文艺委员:我要报名表演唱歌");
}
}
StudyCommitteeMember
package com.nb.java.designmode.delegate.pattern2;
/**
* @Version 1.0
* @program: NB
* @ClassName StudyCommitteeMember
* @author: duanshouzhi
* @create: 2023-07-08 22:51
* @description: 学习委员
**/
public class StudyCommitteeMember implements Student{
@Override
public void registrationProgram(String role) {
System.out.println("我是学习委员:我要报名朗诵古诗");
}
}
Teacher
package com.nb.java.designmode.delegate.pattern2;
/**
* @Version 1.0
* @program: NB
* @ClassName Teacher
* @author: duanshouzhi
* @create: 2023-07-08 23:02
* @description:
**/
public class Teacher {
private Student student;
public Teacher(Student student) {
this.student = student;
}
public void command(String role){
student.registrationProgram(role);
}
}
TestMain
package com.nb.java.designmode.delegate.pattern2;
/**
* @Version 1.0
* @program: NB
* @ClassName TestMain
* @author: duanshouzhi
* @create: 2023-07-08 23:03
* @description:
**/
public class TestMain {
public static void main(String[] args) {
Teacher teacher = new Teacher(new Monitor());
teacher.command("学习委员");
teacher.command("劳动委员");
teacher.command("文艺委员");
}
}
3.运行结果
总结
优点
通过任务委派能将一个统一的入口然后分发给各个子执行器处理,代码可读性强。遵循设计原则:开闭,单一原则。
缺点
任务委派方式是需要根据任务的复杂程度进行不同的改变的,在任务比较复杂的情况下可能需要进行多重委派,容易造成紊乱。