乐趣区

关于云原生-cloud-native:面对复杂业务ifelse-coder-如何升级

作者 | 张建飞  阿里巴巴高级技术专家

导读:针对业务在不同场景下的差别,咱们经常会习惯性地应用 if-else 来实现不同的业务逻辑,长此以往代码越来越难以保护。那么如何打消这些 if-else?面对简单业务应如何思考和剖析?本文分享阿里高级技术专家张建飞(Frank)对于简单业务治理的方法论,介绍一种多维度剖析问题的办法:矩阵分析法。

You should not be a if-else coder, should be a complexity conquer. 
——Frank

这篇文章,是对之前我在《阿里高级技术专家方法论:如何写简单业务代码?》说的“自上而下的结构化合成 + 自下而上的形象建模”方法论的降级。因为在之前的方法论中,咱们短少一个多维度看问题的视角,这种维度思维的缺失,可能会导致 miss 掉一些重要的业务信息,从而使咱们制订软件设计策略的时候,陷入艰难。

有了维度思维,咱们便能够更加方面的去看清业务的全貌,更加全面的掌握业务信息,从而帮忙咱们更加体系化的去治理复杂性。

从 if-else 说起

我常常说,咱们不要做一个 if-else coder。这里的 if-else,不是说咱们在 coding 的时候不能应用 if-else,而是说咱们不应该简陋地用 if-else 去实现业务的分支流程,因为这样随便的代码堆砌很容易堆出一座座“屎山”。

业务的差异性是 if-else 的本源。以批发通的商品业务为例。不同的解决场景,其业务逻辑实现是有差异性的。如下图所示,商品业务的差异性,次要体现在商品类型、销售形式和仓储形式的不同。

这三个维度上的差别组合起来,有 2 3 2 = 12 之多。这就是为什么在老代码中,到处能够看到 if(组合品) blabla,if(赠品) blabla,if(实仓) blabla 之类的代码。

那么,要如何打消这些厌恶的 if-else 呢?咱们能够思考以下两种形式:

  • 多态扩大:利用面向对象的多态个性,实现代码的复用和扩大。
  • 代码拆散:对不同的场景,应用不同的流程代码实现。这样很清晰,然而可维护性不好。

1. 多态扩大

多态扩大能够有继承和组合两种形式。继承勿用多言,组合有点像策略模式,也就是把须要扩大的局部封装、形象成须要被组合的对象,而后对其进行扩大,比方星环的能力扩大点就是这种形式。

这里,咱们举一个继承的例子,商品在上架的时候要查看商品的状态是否可售,普通商品(Item)查看本人就好了,而组合商品(CombineItem)须要查看每一个子商品。

用过程式编码的形式,很容易就能写出如下的代码:

public void checkSellable(Item item){if (item.isNormal()){item.isSellable(); 
        // 省略异样解决
    }
    else{List<Item> childItems = getChildItems();
        childItems.forEach(childItem -> childItem.isSellable()); 
        // 省略异样解决
    }
}

然而,这个实现不优雅,不满足 OCP,也短少业务语义显性化的表白。更好的做法是,咱们能够把 CombineItem 和 Item 的关系通过模型显性化的表达出来。

这样一来,一方面模型正确的反馈了实体关系,更清晰了。另一方面,咱们能够利用多态来解决 CombineItem 和 Item 的差别,扩展性更好。重构后,代码会变成:

public void checkSellable(Item item){if (!item.isSellable()){throw new BizException("商品的状态不可售,不能上架");
    }
}

2. 代码拆散

所谓的代码拆散是指,对于不同的业务场景,咱们用不同的编排代码将他们离开。以商品上架为例,咱们能够这样写:

/**
* 1. 普通商品上架
*/
public void itemOnSale(){checkItemStock();// 查看库存
    checkItemSellable();// 查看可售状态
    checkItemPurchaseLimit();// 查看限购
    checkItemFreight();// 查看运费
    checkItemCommission();// 查看佣金
    checkItemActivityConflict();// 查看流动抵触

    generateCspuGroupNo();// 生成单品组号
    publishItem();// 公布商品}

/**
* 2. 组合商品上架
*/
public void combineItemOnSale(){checkCombineItemStock();// 查看库存
    checkCombineItemSellable();// 查看可售状态
    checkCombineItemPurchaseLimit();// 查看限购
    checkCombineItemFreight();// 查看运费
    checkCombineItemCommission();// 查看佣金
    checkCombineItemActivityConflict();// 查看流动抵触

    generateCspuGroupNo();// 生成单品组号
    publishCombineItem();// 公布商品}

/**
* 3. 赠品上架
*/
public void giftItemOnSale(){checkGiftItemSellable();// 查看可售状态
    publishGiftItem();// 公布商品}

这种形式,当然也能够打消 if-else,彼此独立,也还清晰。但复用性是个问题。

3. 多维分析

仔细的你可能曾经发现了,在下面的案例中,普通商品和组合商品的业务流程根本是一样的。如果采纳两套编排代码,有点冗余,这种反复将不利于前期代码的保护,会呈现散弹式批改(一个业务逻辑要批改多处)的问题。

一个极其状况是,如果普通商品和组合商品,只有 checkSellable() 不一样,其它都一样。那毫无疑问,咱们应用有多态(继承关系)的 CombineItem 和 Item 来解决差别,会更加适合。

而赠品上架的状况恰恰相反,它和其余商品的上架流程差别很大。反而不适宜和他们合用一套流程代码,因为这样反而会减少别人的了解老本。还不如独自起一个流程来的清晰。

那么,问题来了,咱们什么时候要用多态来解决差别,什么时候要用代码拆散来解决差别呢?

接下来,是我明天要给你着重介绍的多维度剖析问题的方法论之一:矩阵分析法。

咱们能够弄一个矩阵,纵列代表业务场景,横列代表业务动作,外面的内容代表在这个业务场景下的业务动作的具体业务流程。对于咱们的商品业务,咱们能够失去如下的矩阵:

通过下面的矩阵剖析,咱们不难看出一般品和组合品能够复用同一套流程编排代码,而赠品和出清品的业务绝对简略,更适宜有一套独立的编排代码,这样的代码构造会更容易了解。

维度思维

1. 多维度的重要性

下面的案例不是我假造进去的,而是我在和张文(我共事)探讨应该用哪种形式去解决业务差别的实在故事。

我记得在和大学探讨完,开车回去的路上,我始终在想这个问题,而后在第二个路口等红灯的时候,忽然有一个灵感冒进去。我克制不住兴奋,一边开车,一边发消息给张文说:“我想到了一个很 NB 的方法论,能解决在‘多态扩大’和‘代码拆散’之间如何做抉择的问题”。

其实,我晓得我兴奋的不仅仅是解决了这个问题。我兴奋的是,我第一次真正领悟到了多维度思考的重要性。从而有机会从一个“单维度”生物,升级成一个“多维度”思考者。妈妈再也不必放心我被“降维打击”了 :)

结构化思维有用、很有用、十分有用,只是它更多关注的是单向维度的事件。比方我要拆解业务流程,我要合成老板给我的工作安顿,我要梳理测试用例,都是单向维度的。

而复杂性,通常不仅仅是一个维度上的简单,而是在多个维度上的穿插复杂性。当问题波及的因素比拟多,彼此关联关系很简单的时候,两个维度必定会比一个维度要来的清晰,这也是为什么说矩阵思维是比结构化思维更高层次的思维形式。

实际上,咱们从汉语的词汇上,也不难看出一个人的思维层级,是和他的思考维度正相干的。当咱们说这个人很“轴”、“一根筋”的时候,实际上是在说他只有一维的线性思维。所以,察看事物的视角越多,维度越丰盛,其思维层级也会越高。

2. 无处不在的多维思考

有了这些感悟,我开始零碎的整顿对于多维度思考剖析的材料,发现这种思考形式真是无处不在。发现的越多,我越是感叹,为什么如此重要的思维形式,我到当初才领悟到。

1)波士顿矩阵

比方,在做产品剖析的时候,有对产品发展前景进行剖析的波士顿矩阵。

2)订单因素剖析

当年,我在 1688 做交易下单业务的时候,有十分多的下单场景,每种场景下,买家享受的权利是不一样的(如下表所示)。咱们过后也是应用了矩阵去表白这个简单的关系,只是过后还没有想到要将其晋升到方法论的高度。

3)数据穿插剖析

在数据分析中,维度剖析是十分重要的,特地是维度很多的时候,咱们能够通过皮尔逊积矩相关系数,做穿插剖析,从而补救独立维度剖析没法发现的一些问题。


简略相关系数矩阵

4)剖析矩阵

最近我碰巧看到 Alan Shalloway 写的《设计模式解析:Design Patterns Explained》,这是一本十分经典的对于 OOP 的书,外面的第十六章就是专门讲“剖析矩阵”的,作者发明这个方法论的初衷也是因为业务波及的因素太多,信息量太大,他须要一种组织海量数据的新形式。

我和 Alan 的门路不一样,然而都得出了同样的论断。由此可见,这种矩阵剖析的形式确实是对简单业务进行剖析的一把利器,业务场景越多,穿插关系越是简单,越须要这样的剖析。

5)组织阵型

生产关系决定生产力,对于一个管理者来说,如何无效的设置组织构造是决定团队是否能高效合作的要害。所以咱们能够看到公司外面,每年都有比拟大的对于组织构造和人员安顿的调整。

对于技术团队来说,咱们习惯于按畛域划分工作范畴,这样做的益处是责任到人、职责清晰。然而,畛域只是一个维度,咱们工作通常都是以我的项目的模式的发展,而我的项目通常是贯通多个畛域的。所以,在做团队组织布局的时候,咱们能够通过业务畛域和业务我的项目两个维度去看。

比方,在我负责的商品团队,我会依照如下的模式去做职责划分。

6)工夫维度

除了工作,生存中也到处可见多维思考的重要性。

比方,咱们说节约可耻,应该把盘子舔的很洁净,岂不知加上工夫维度之后,你以后的舔盘,前面可能要消耗更多的资源和精力去减肥,反而会造成更大的节约。

咱们说代码写的俊俏,是因为要“疾速”撑持业务,加上工夫维度之后,这种长期的斗争,换来的是意想不到的 bug,线上故障,以及无止尽的 996。

7)RFM 模型

简略的思考是“点”状的,比方舔盘、代码堆砌就是当下的“点”;好一点的思考是“线”状,加上工夫线之后,不难看出“点”是有问题的;再全面一些的思考是“面”(二维);更体系化的思考是“体”(三维);比方,RFM 模型就是一个很不错的三维模型。惋惜的是,在表白上,咱们人类只能在二维的空间里去模仿三维,否则四维可能会更加有用。

简单业务治理总结

在前言局部,我曾经说过了,多维分析是对之前方法论的降级。加上以前的方法论,残缺的方法论应该是“业务了解 –> 领域建模 –> 流程合成 –> 多维分析”。

为了不便大家了解,上面我把这些方法论做一个简略的串联和解释。

1. 业务了解

了解业务是所有工作的终点。首先,咱们要找到业务的外围因素,了解外围概念,梳理业务流程。

比方,在批发通的商品域,咱们要晓得什么是商品(Item),什么是单品(CSPU),什么是组合品(CombineItem)。在下单域,咱们要晓得订单(order)的形成因素是商品、优惠、领取。在 CRM 畛域,咱们要了解客户、机会、联系人、Leads 等等。

这里,我想再次强调下语言的重要性,语言是咱们思考的载体,就像维特根斯坦说的:“但凡可能说的事件,都可能说分明”。

你不应该放过任何一个含糊的业务概念,肯定要透彻的了解它,并给与正当的命名(Ubiquitous Language)。唯有如此,咱们能力更加清晰的了解业务,能力更好的发展后续的工作。

2. 领域建模

在软件设计中,模型是指实体,以及实体之间的分割,这里须要咱们具备良好的形象能力。可能透过庞杂的表象,找到事务的实质外围。

再简单的业务畛域,其外围概念都不应该太简单,抓住了外围,咱们就抓住了主线,业务往往都是围绕着这些外围实体开展的。

比方,商品域尽管很简单,但其外围的畛域模型,无外乎就如下图所示:

3. 流程合成

对于流程合成,在《阿里高级技术专家方法论:如何写简单业务代码?》外面曾经有十分具体的论述,这里就不赘述了。

简略来说,流程合成就是对业务过程进行具体的合成,应用结构化的方法论(先演绎、后演绎),最初造成一个金字塔构造。

比方,在商品畛域,有创立商品、商品上架、上架审核、商品下架、下架审核、批改商品、删除商品等一些列动作(流程),每个动作的背地都有非常复杂的业务逻辑。咱们须要对这些流程进行具体的梳理,而后按步骤进行合成。最初造成一个如下的金字塔构造:

4. 多维分析

对于多维分析,我以二维的矩阵剖析为例,我想我后面应该曾经说分明了。

业务的复杂性次要体现在流程的复杂性和多维度因素互相关联、依赖关系上,结构化思维能够帮咱们梳理流程,而矩阵思维能够帮忙咱们梳理、出现多维度关联、依赖关系。二者联合,能够更加全面的展示简单业务的全貌。从而让咱们的治理能够对症下药、有章可循。

既然是方法论,在这里,我会尝试给出一个矩阵剖析的框架。试想下,如果咱们的业务很简略,只有一个业务场景,没有分支流程。咱们的零碎不会太简单。之所以简单,是因为各种业务场景相互叠加、依赖、影响。

因而,咱们在做矩阵剖析的时候,纵轴能够抉择应用业务场景,横轴是备选维度,能够是受场景影响的业务流程(如文章中的商品流程矩阵图),也能够是受场景影响的业务属性(如文章中的订单组成因素矩阵图),或者任何其它不同性质的“货色”。

通过矩阵图,能够清晰的展示不同场景下,业务的差异性。基于此,咱们能够定制满足差异性的最佳实现策略,可能是多态扩大,可能是拆散的代码,也可能是其它。

这就是矩阵剖析的要义,其本质是一种多维度思考的方法论。

篇后寄语

最初,我想说世界是熵增的(即万物都在迟缓的土崩瓦解),管制复杂度是咱们这些从业者无奈推卸的责任和使命。

软件行业的倒退才几十年,还是一门年老的学科,软件工程就像一个刚学会走路的小孩,还很不成熟,有时还很童稚。

但毕竟还是有几十年的积淀,还是有一些好的办法和实际能够参考,我的这些总结积淀只是在前人的根底上,多走了一点点而已。但就是这一点点,也实属来自不易,其中冷暖,只有本人能领会。能够说,这一路走来,是一场对心力、脑力和膂力的继续考验。

  • 心力 是指不将就的匠心,不斗争的信心,不满足的好奇心、以及不放弃的恒心。
  • 脑力 是指那些必要的思维能力、学习能力、思考能力、思辨能力。
  • 之所以说“业务了解 –> 领域建模 –> 流程合成 –> 多维分析”是 膂力,是因为实现它们就像是在做填空题,只有你违心花工夫,再简单的业务都能够循序渐进的清晰起来。

梳理清晰了,再配合 COLA(https://start.aliyun.com/)的领导,咱们就有可能写出清晰、易读的代码,就有可能从一个 if-else coder 降级为一个 complexity conquer。

而这不正是咱们工程师手不释卷的谋求吗?

“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术畛域、聚焦云原生风行技术趋势、云原生大规模的落地实际,做最懂云原生开发者的公众号。”

退出移动版