设计模式&委派模式(Delegate Pattern)

代码地址: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.运行结果

在这里插入图片描述


总结

优点

通过任务委派能将一个统一的入口然后分发给各个子执行器处理,代码可读性强。遵循设计原则:开闭,单一原则。

缺点

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值