故事
小猫的风波已经过去了,这几天,小猫在安安心心地撸着系统现状方案,准备着下次月会的分享。
这天,原本静谧而又和谐的办公室却被开放平台老六抱怨声打破了。
“不改,别给我打电话了!说几遍都没用。这是一个研发的底线.....”
没过一会,产品老汪担心老六对其"对脸开大",孙子似地提着杯咖啡找到了老六。老汪是明事理的产品经理,为人处事儿这方面没得说。
“这事儿,我也为难,兄弟,帮帮忙,来喝杯咖啡解解乏。我也知道这种客户很难搞,但是我们是乙方,没办法,这年头大环境摆在这里,赚钱不容易,大家互相体谅一下。”
老六接过老汪的咖啡,气呼呼地抿了一口。
“上次发布商品的时候让我把修改商品属性和新增商品信息放到一个接口也就算了,这次还让我干脆把上架到货架直接包到一起?那后面我们这接口还咋维护了?后面是不是把商品添加到活动中也往这一个接口上堆啊?你让我到后面咋维护么?他们公司的lowb研发懂不懂软件设计原则啊......”
产品老汪在旁边连连点头,"兄弟,消消气,消消气"。
“要不这样吧,咱们拉上对面研发一起聊聊吧,看看双方是否都可以让让步......”。
于是老汪和老六一起来到一间会议室,约客户开始了在线会议。
单一职责原则
大家有没有遇到老六一样的遭遇。由于业务要求,接口或者某个模块中耦合了太多可能不相干的事情。在这里你们是如何处理的呢?关于这点咱们要引出单一职责原则这样一个软件设计原则。
对于单一职责原则,官方术语:单一职责原则,英文缩写SRP,全称Single Responsibility Principle。There should never be more than one reason for a class to change。一个类或模块应该有且只有一个改变的原因。如果一个类拥有多个职责,这些职责之间的耦合会导致系统变得不稳定和难以维护。
在OOP里面,高内聚、低耦合是软件设计追求的目标,而单一职责原则可以看做是高内聚、低耦合的引申,将职责定义为引起变化的原因,以提高内聚性,以此来减少引起变化的原因。职责过多,可能引起变化的原因就越多,这将是导致职责依赖,相互之间就产生影响,从而极大的损伤其内聚性和耦合度。单一职责通常意味着单一的功能,因此不要为类实现过多的功能点,以保证实体只有一个引起它变化的原因。
可见无论从官方定义,还是对“单一职责”名称的解释,都能很好的理解单一职责原则的意义。其实在软件设计中,要真正用好单一职责原则并不简单。
老猫觉得如果需要遵循这样的原则,最关键的地方还是在于职责的划分。不过说到这个职责划分又是比较偏向于业务性质的,其和产品需求是分不开关系的。咱们就拿老六遇到的这个事情来分析一下。
一个发布商品的例子
说明:下面demo的表现形式,咱们都会用到类图的方式,关于类图的相关知识点,大家有兴趣可以看这里“类图知识点”。
第一版
咱们一起看一下这个例子,如下图:
接口
上面的图中,我们看到了有一个发布商品的接口类以及实现。在其中,我们看到其中包含了发布商品的基础信息,发布图片信息,发布规格信息,将商品加入商品池,将商品加入售货架,将商品加入某个活动。
我们一起来看一下上述的设计是否存在问题?很多时候其实是有争议的。
单一职责原则要求一个接口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情,原则上来说,单纯从客户角度,如果能保证客户后续需求不会变更,以商品发布作为颗粒度,那么它是合理的。因为业务上已经约定好,里面有商品属性信息维护,有商品行为信息维护。如果没有新的业务概念提出来,顶多后续内部改造的也就是属性变更以及上下架和商品池维护变更。这种角度来说是合理的。
但是这种不变更的保证谁能担保呢?另外接口也不是针对这一家客户开放的,当然考虑通用性。
第二版
这不没多久业务又接了一家新客户,他们的要求是发布商品就是商品信息的发布。剩余行为无需做强绑定,上下架行为由对方运营人员选择性执行,没必要新品一发就上架。那现在的这套就打又破了之前的单一原则。因为由于业务的要求,咱们要将行为拆分成下面这种模式:
第一次拆分
上面的业务看起来更加清晰一些,咱们把属性同步设置单独抽离,针对操作商品的行为也单独封装为另外一个行为接口。系统功能可拓展,接口可复用的角度来说,无论是第一个版本还是第二个版本,看起来都比较适用。这么一来,看起来两个客户的业务都遵循了单一职责的原则。虽然这种方案会引来第一个客户的研发的不满,因为对于他们来说可能会调用两次(当然我们也可以通过门面模式将其整合,当然这是后话),但是站在系统本身的设计角度来说,是比较合理的。但是这样的一个抽取方式真的够了么?
第三版
又来了一家客户,由于对方公司有自己的运营想法,对方不希望用我们的活动,他们希望有自己的活动,并且需要我们给其单独定制,那么此时咱们又发现,单一职责的这个设计原则又被打破了,因为我们需要针对活动去做定制,为了遵循职责单一原则,所以这时候需要我们将活动行为单独剥离。然后就有了下面这样的情况。
第三次拆分
这次的接口看起来更加灵活,满足单一模式的同时,满足了以上所有的业务。但是这就够了么?
显然不够,我们还是会遇到各种业务需求的变动,但是上述的抽取在当前的业务下面看起来是比较适用的。能够cover住大部分的场景了。对于后续的业务拓展也比较友好。
总结
上述的例子比较极端,老猫其实主要想和大家一件事情,所谓的单一职责的软件设计模式并不是绝对的,我们会根据业务的需求形态做出动态调整。如何遵循好单一职责的设计原则,其实还是需要我们能够对业务有一个比较精准的领域划分。小伙伴们,你们觉得呢?