一、委派模式的定义

委派模式的基本作用是负责任务的调度和分配任务。

委派模式 在现实生活中也有相关的体现,比如一个具体任务的执行,首先是经过高层领导讨论,将具体的任务委派给相关部门经理,部门经理拿到任务通知后去委派相关的员工具体执行任务,委派任务的角色实际并非具体执行任务,而是对任务分发,执行过程进行管理。

委派模式 是一种行为型模式。 在Spring 中应用挺多的,但是不属于(GOF)23种设计模式。

二、优缺点

优点:

对内隐藏实现, 简化调用。

缺点:

当实际处理业务的类需要扩展时,派发命令的类也需要对应做调整,不符合开闭原则。

三、区别

代理模式 与 委派模式的区别

代理模式 注重的是 过程, 委派模式 注重的是 结果

策略模式 注重是可扩展(外部扩展),委派模式 注重内部的灵活和复用;

委派的核心:就是分发、调度、派遣;

委派模式:就是静态代理和策略模式一种特殊的组合;

四、角色与UML类图

4.1、主要角色:

  • Task (抽象任务): 定义一个抽象接口, 它有多个实现类。

  • ConcreteTask (具体任务执行者、被委派者): 真正执行任务的角色。

  • Delegate (委派者): 负责在各个具体角色实例之间做出决策,判断并调用 ConcreteTask 的方法。

4.2、UML类图

在这里插入图片描述

说明: 有的书上说 Delegate 不实现 Task 接 口 。

五、示例1:

5.1、参考类图:

在这里插入图片描述

5.2、代码:

1、BaseService( 任务接口)
/**
 * 公共的服务接口
 */
public interface BaseService {
    // 执行命令的方法
    void execute();
}
2、具体任务(FrontService、BackendService)

前台Service:

/**
 * 前台Service
 */
public class FrontService implements BaseService {

    @Override
    public void execute() {
        System.out.println("执行前端页面设计任务");
    }
}

后台Service:

/**
 * 后台Service
 */
public class BackendService implements BaseService {

    @Override
    public void execute() {
        System.out.println("执行后台代码开发任务");
    }
}
3、Delegator:委派者角色

委派者角色,负责委派,分发任务,最后提交执行的结果。

import java.util.HashMap;
import java.util.Map;

/**
 *  委派任务角色
 */
public class Delegator {
    
    /**  用一个容器存放不同的子任务对应的执行对象*/
    Map<String, BaseService> map = new HashMap<>();

    // 任务的委派,分发
    private BaseService delegate(String msg) {
        map.put("font", new FrontService());
        map.put("backend", new BackendService());
        if ("backend".equals(msg)) {
            return map.get(msg);
        } else if ("font".equals(msg)) {
            return map.get(msg);
        }
        System.out.println("委派任务出错!");
        return null;
    }

    // 任务具体的执行
    public void execute(String msg) {
        if (delegate(msg)!=null) {
            delegate(msg).execute();
        }
    }

}
4、Client
/**
 *  客户端 ->委派任务
 */
public class Client {

    public static void main(String[] args) {

        String command1 = "font";
        String command2 = "backend";
        String command3 = "test";
        
        Delegator delegator = new Delegator();
        delegator.execute(command1);
        delegator.execute(command2);
        delegator.execute(command3);
    }
}
5、效果截图:
执行前端页面设计任务
执行后台代码开发任务
委派任务出错!

六、示例2

6.1、需求:

老板( Boss ) 给 组长 ( Leader ) 下达任务,组长 ( Leader ) 根据实际情况给每个员工派发工作任务, 等到员工把工作任务完成之后, 再由组长 ( Leader ) 将 汇报工作进度和结果给 老板( Boss )。

在这里插入图片描述

6.2、代码:

1、Worker:委派者和被委派者共同的接口

实现一个 Worker 接口,这个是作为 委派者 和 被委派者 共同的接口。

/**
 * 工人接口
 */
public interface Worker {

    void doing(String thing);
}
2、普通工人( 厨师工、缝纫工 ):被委派者
/**
 * 厨师工
 */
public class ChefWorker implements Worker {
   
    @Override
    public void doing(String thing) {
        System.out.println("我是厨师,我接到任务,需要做美食。");
    }
}


/**
 * 缝纫工
 */
public class TailorWorker implements Worker {
   
    @Override
    public void doing(String thing) {
        System.out.println("我是缝纫工,我接到任务,需要做衣服。");
    }
}
3、组长 (Leader): 委派发者
/**
 * 组长
 */
public class Leader implements Worker {

    private static Map<String, Worker> workerMap = new HashMap<>();

    public Leader() {
        workerMap.put("cooking", new ChefWorker());
        workerMap.put("sewing", new TailorWorker());
    }

    /**
     * 做事情
     *
     * @param thing 任务
     */
    @Override
    public void doing(String thing) {
        System.out.println("我是组长,老板派活了: " + thing + ",我来安排任务: " + thing);
        if (workerMap.containsKey(thing)) {
            workerMap.get(thing).doing(thing);
        } else {
            System.err.println("目前办公室没有对应职位的工人");
        }
    }
}
4、老板 (Boss) 下命令:

老板( Boss )将工作分给组长。

/**
 * 领导
 */
public class Boss {

    /**
     * 下命令
     *
     * @param thing  需要处理的事情
     * @param leader 安排给一个组长
     */
    public void command(String thing, Leader leader) {
        System.out.println("我是老板,我现在需要处理: " + thing);
        leader.doing(thing);
    }
}
5、Client
/**
 *  测试类
 */
public class Client {
    public static void main(String[] args) {
        Boss boss = new Boss();
        // 做饭
        boss.command("cooking", new Leader());        
        System.out.println("================================");
        // 缝纫
        boss.command("sewing", new Leader());
        // 销售
        System.out.println("================================");
        boss.command("selling", new Leader());
    }
}
6、运行结果:
我是老板,我现在需要处理: cooking
我是组长,老板派活了: cooking,我来安排任务: cooking
我是厨师,我接到任务,需要做美食。
================================
我是老板,我现在需要处理: sewing
我是组长,老板派活了: sewing,我来安排任务: sewing
我是缝纫工,我接到任务,需要做衣服。
================================
我是老板,我现在需要处理: selling
我是组长,老板派活了: selling,我来安排任务: selling
目前办公室没有对应职位的工人

七、委派模式 在开源框架中的使用

委派模式大量使用在spring、mybatis等开源框架中,理解委派模式的实现原理可以更好理解这些框架源码。委派模式的核心是 委派类 的实现
代码中,一般 DelegateDispatcher 结尾的都是委派类。
在Spring源码中我们可以搜索一下,使用 delegate 关键词模糊查找到如下的类:
BeanDefinitionParserDelegateConstructorDelegateMultipartResolutionDelegateTypeConverterDelegate 等。

我们熟知的 DispatcherServlet 虽然没带 delegate ,但也是委派模式的一种实现。
前端请求都统一走到 DispatcherServletdoService() 方法中,然后在 doService() 方法中调用 doDispatch() 方法,在 doDispatch() 方法中,进行复杂的任务进行分解,分发执行并通过一个委派者角色,会获取业务处理的 handler ,执行 handle() 方法处理请求结果。

DispatcherServletdoDispatch() 方法截图:在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐