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

cover

前情提要

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

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

所有示例源码已经上传到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, 谢谢.


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容