采购申请的处理流程 --- 责任链模式

2,179 阅读7分钟

前情提要

上集讲到小光梳理了公司的组织架构, 利用组合模式建立起了一个可扩展变化的多层的组织架构体系. 更清晰地明确了公司各个层级, 各个部门的职责. 大家明确职责, 通力合作, 让"小光热干面"这个招牌越做越好.

然而, 小光毕竟是经历过几任大企业的人啊, 弄好组织结构只是小光企业管理的第一步, 接下来小光准备要梳理下工作流了.

所有示例源码已经上传到Github, 戳这里

比如采购的审批流程

刚开始, 小光只有光谷一个店的时候, 很多时候采购某些东西也就没有什么流程, 大家一商量, 小光拍板, 就派人去采购了. 然而, 现在发展到好几个分店了, 小光肯定不能做到事事过问了, 故而会有一些放权, 但是某些情况(大件的采购)小光肯定还是想要到自己这里审批的.

想到这些, 小光想着第一个需要建立的就是这个采购审批流程了.

做一个采购审批系统

先分析问题

那么该怎么做呢?
正所谓问对了问题, 也就解决了一半. 小光一贯的做法, 是先想清楚问题是什么, 然后再针对性的出解决方案.

那么, 再回头看看, 问题具体是什么呢?

  1. 一个采购申请的审批流程系统
  2. 不同物件采购的审批流程并不是一样的(有的可能会到小光那儿, 有的没有必要)
  3. 同一个审批流程也可能会有变化(如上期组织结构所言, 公司组织结构会不断调整, 肯定会影响到采购审批层级的变化).

相应的解决点

问清楚了问题, 小光针对性的想了下方案的特性:

  1. 首先肯定是一个流程化的东东, 也就是说类似流水线的处理审批单. 例如采购员提出采购单, 采购部经理审批盖章, 到总经理审批...

  2. 不只是一条这样的流水线, 不同类型审批单的流水线不一致, 故而应该是有几条流水线.

  3. 因为流水线上的操作员(由于公司层级的调整, 部门变化)会变化, 故而流水线的每个节点最好是可定制的.

解决之道

了解了问题及其对应的特性, 小光开始了自己的采购审批系统设计.

首先, 对于第一个特性, 小光心想, 这很简单, 实际上就是一个指向性的调用嘛, A调用B, B再调用C...如此这般即可.

当然, 小光面向对象的编程思想已经深入脑髓, 他将采购员, 采购经理, 总经理都对象化了. 另外, 采购经理和总经理以及小光, 都视为是可以处理采购申请的人, 故而抽象出了一个RequestHandler:

RequestHandler(采购申请处理人)

public abstract class RequestHandler {

    // 上一级的处理人员
    private RequestHandler mNext;

    public RequestHandler(RequestHandler next) {
        this.mNext = next;
    }

    /**
     * 处理采购需求
     * @param req
     */
    public void handleRequest(Request req) {
        printHandling(req);
        if (mNext != null) {
            mNext.handleRequest(req);
        }
    }

    protected void printHandling(Request req) {
        System.out.println(this.toString() + "审批了:" + req);
    }

    @Override
    public abstract String toString();
}

采购经理:

public class PurchasingManager extends RequestHandler {

    /**
     * 构造时, 传入下一个处理人
     * @param next 下一个处理人
     */
    public PurchasingManager(RequestHandler next) {
        super(next);
    }

    @Override
    public String toString() {
        return "采购部经理";
    }
}

总经理和小光的实现与采购经理类似, 在此略过, 大家可以参看github上的源码.

为了满足问题的第2点:

不同物件采购的审批流程并不是一样的(有的可能会到小光那儿, 有的没有必要)

还需要有一些定制的流程:

public class RequestFlow {

    public static final int TYPE_SMALL_REQUEST = 1;
    public static final int TYPE_NORMAL_REQUEST = 2;
    public static final int TYPE_BIG_REQUEST = 3;

    public static RequestHandler getRequestChain(int type) {
        switch (type) {
            // 小物件, 只需采购经理审批
            case TYPE_SMALL_REQUEST:
                return new PurchasingManager(null);

            // 一般物件, 需要总经理审批
            case TYPE_NORMAL_REQUEST:
                return new PurchasingManager(new GeneralManager(null));

            // 大件物品, 需要小光审批
            case TYPE_BIG_REQUEST:
            default:
                return  new PurchasingManager(new GeneralManager(new XiaoGuang(null)));
        }
    }
}

让我们看看采购员的请求方式:

// 采购员
public class Buyer {

    public static void main(String[] args) {

        // 小需求
        Request smallRequest = new Request("10箱饮料");
        RequestFlow.getRequestChain(RequestFlow.TYPE_SMALL_REQUEST).handleRequest(smallRequest);

        // 一般需求
        Request normalRequest = new Request("10套桌椅");
        RequestFlow.getRequestChain(RequestFlow.TYPE_NORMAL_REQUEST).handleRequest(normalRequest);

        // 大需求
        Request bigRequest = new Request("一套同步电子显示大屏");
        RequestFlow.getRequestChain(RequestFlow.TYPE_BIG_REQUEST).handleRequest(bigRequest);

    }
}

// 小需求 输出:
采购经理审批了:10箱饮料采购申请

// 一般需求  输出:
采购经理审批了:10套桌椅采购申请
总经理审批了:10套桌椅采购申请

// 大需求 输出:
采购经理审批了:一套同步电子显示大屏采购申请
总经理审批了:一套同步电子显示大屏采购申请
小光审批了:一套同步电子显示大屏采购申请

可以看到, 小光目前设计的这套系统是可以业务审批流程的需求的. 我们这里RequestFlow实际上采用简单工厂的模式, 适用于流程数比较少的情况. 如果考虑到后续的组织架构的变化影响什么的, 这块是值得重构一下的.


另外一个可优化的是, 我们实际上并不需要这么多的流水线, 可能只需要定制最长的那条, 然后在每个节点上根据传过来的审批请求来决定是否在当前节点就消化掉, 还是说还要传给上一级处理. 大家可以自己尝试修改下~~

故事之后

故事情节相对简单, 然而确确实实这个就是责任链模式的一个应用. 照例, 我们上UML类图, 看下各个部分的关系(为了清晰展示链的关系, 类图中国舍弃RequestFlow这个工厂):

责任链模式
很多对象由每一个对象对其下家的引用(mNext)而连接起来形成一条链. 请求在这个链上传递, 直到链上的某一个对象决定处理此请求.
行为解耦的一种模式, 调用者并不确切知道也无需知道行为的执行者是谁.

扩展阅读一

开发Android的同学可能都用过大名鼎鼎的OkHttp了, OkHttp的核心其实就是其拦截器链(interceptor chain)的实现. 个人认为其最巧妙的也是这个拦截器链的实现了, 完美契合.

让我们先简单缕下OkHttp的request流程:

简单分析CacheInterceptor(OkHttp的缓存实现):

  @Override public Response intercept(Chain chain) throws IOException {
    ...

    // 取缓存的Response
    Response cacheResponse = strategy.cacheResponse;

    // 如果缓存满足要求, 直接返回. 相当于采购经理能够审批该采购请求, 无需上报给总经理.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    // 如果不能, 则交给链路的下一个责任人来处理.
    try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
      ...
    }

    // 对一个链点返回的response做处理, 看是否要缓存起来.
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (HttpHeaders.hasBody(response)) {
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      response = cacheWritingResponse(cacheRequest, response);
    }

    return response;
  }

大家可以看到, 只是一个典型的责任链模式的运用.
牛逼的是, OkHttp中的拦截器(interceptor)不仅仅是单向的一个链点, 而是一个双向的回路. 每个链路节点, 在request的过程中会做一次拦截处理, 诸如是否直接返回缓存, 加上统一的UserAgent等; 在response回来之后会再做一次拦截处理, 例如缓存, 根据response header做相关处理等. 如下:

okhttp

强烈建议Android开发的同学可以深入研究下OkHttp的interceptor链这块的处理, 肯定是受益匪浅~


搭建后初步的审批体系, 小光又开始畅想未来了~~


最近工作真的是太忙了, 本系列延期了又延期, 抱歉.


借自己的广告位, 广告一波
大量高级Android坑位求自荐, 求推荐.
关键词:
alibaba.com这个App的开发
坐标杭州
3年及以上Android平台开发经验
简历请发至anly_jun@163.com, 谢谢.