职责单一原则作为面向对象的SOLID原则之首,可见其重要性了,职责单一原则使得软件系统更易于开发,测试和维护,在设计架构上也体现了高内聚低耦合的特性。
面向对象的S.O.L.I.D 原则 一般来说这是面向对象的五大设计原则,但是,我觉得这些原则可适用于所有的软件开发。 Single Responsibility Principle (SRP) – 职责单一原则 关于单一职责原则,其核心的思想是:一个类,只做一件事,并把这件事做好,其只有一个引起它变化的原因。单一职责原则可以看作是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。职责过多,可能引起它变化的原因就越多,这将导致职责依赖,相互之间就产生影响,从而极大的损伤其内聚性和耦合度。单一职责,通常意味着单一的功能,因此不要为一个模块实现过多的功能点,以保证实体只有一个引起它变化的原因。 Unix/Linux是这一原则的完美体现者。各个程序都独立负责一个单一的事。 Windows是这一原则的反面示例。几乎所有的程序都交织耦合在一起。
微服务是目前软件开发的一个热点,微服务和容器化的架构下,职责单一原则就显得更重要了,因为相比于之前单块的架构,微服务架构是由一系列职责单一的细粒度服务构成的分布式网状结构。
例如目前碰到的一个业务问题:在私有云的纳管平台下需要考虑到用户的配额问题,避免超量使用和购买。这个配额问题就需要考虑职责单一,即在下订单的时候检查并扣除配额,激活失败后返还配额。那么关于配额的所有操作都得放在订单这个微服务里面做,扩散到其他任何微服务都是不合适的(一个微服务,只做一件事,并把这件事做好),扩散到其他微服务会带来不必要的通讯代价和维护成本。
1. 先用Visio的Basic Flowchart Shapes画出流程图,如下:
图中使用MQ的方式保证了订单模块收到了激活成功或失败的消息并处理配额,形成闭环。
2. 再通过Java代码实现,如下:
private void updateQuota(String orderId,byte orderType,String resultType){ if (ResultType.OK.name().equals(resultType)) { // 激活成功 if (orderType == OrderType.PurchaSEOrder.getValue().byteValue()) { orderMgmtService.updatePltQuotaByOrderId(orderId,(byte) 0); //扣除平台配额 } else if (orderType == OrderType.CancelOrder.getValue().byteValue()) { orderMgmtService.updateOrgQuotaByOrderId(orderId,(byte) 1); //返还部门配额 orderMgmtService.updatePltQuotaByOrderId(orderId,(byte) 1); //返还平台配额 } else if (orderType == OrderType.ChangeOfferOrder.getValue().byteValue()) { String oldOrderId = getOldOrderId(orderId); // 获取oldOrderId if(StringUtils.isNotEmpty(oldOrderId)){ orderMgmtService.updateOrgQuotaByOrderId(oldOrderId,(byte) 1); //返还旧部门配额 orderMgmtService.updatePltQuotaByOrderId(oldOrderId,(byte) 1); //返还旧平台配额 orderMgmtService.updatePltQuotaByOrderId(orderId,(byte) 0); //扣除新平台配额 } } } else { // 激活失败 if (orderType == OrderType.PurchaSEOrder.getValue().byteValue()) { orderMgmtService.updateOrgQuotaByOrderId(orderId,(byte) 1); //返还部门配额 } else if (orderType == OrderType.ChangeOfferOrder.getValue().byteValue()) { orderMgmtService.updateOrgQuotaByOrderId(orderId,(byte) 1); //返还新部门配额 } } }3. 紧接着先对如下的2个接口做单元测试:
void updateOrgQuotaByOrderId(String orderId,byte actionType); void updatePltQuotaByOrderId(String orderId,byte actionType);
4. 然后是集成测试和迭代交付。
最后想分享一下编程了这么多年的一个心得
大家一提到程序员,首先想到的是以下标签:苦逼,加班,熬夜通宵。但是,但凡工作了的同学都知道,其实大部分程序员做的事情都很简单,代码CRUD可以说毫无技术含量,就算什么不懂依葫芦画瓢很多功能也能勉强做出来,做个多线程并发就算高科技了,程序员这行的门槛其实还是比较低的。(这里说的是大部分,有些牛逼的,写算法、jvm等的请自动跳过) 是不是觉得很矛盾,一方面工作不复杂,一方面却累成狗。有没有想过问题出在哪里?有没有想过时间都花在哪里呢? 对于我个人来说,编码还是一个相对轻松的活(我是负责公司it系统的,没有太多技术含量,数据量大,但并发量不大)。从工作到现在,我加班编码的时间还是比较少的,我到现在为止每天还会编码,很少因为编码工作加班。 大家写的东西都是一些crud的业务逻辑代码,为什么大家这么累,加班加点天天都是奋斗者?我从自己带的项目中观察中发现,大部分人的大部分时间都是在 定位问题 + 改代码,真正开发的时间并不多。定位问题包括开发转测试的时候发现问题和上线后发现问题,改代码的包括改bug和因为需求变动修改代码(后面专门开一贴说如何应对需求改动)。 所以说,simple is not easy。很多人就是因为觉得简单,所以功能完成自己测试ok了就算了,没有思考有没有更加好的方式。归根到底是因为编码习惯太糟糕,写的代码太烂,导致无法定位频繁修改频繁出问题。 其实,对于个人来说,技术很重要,但是对于工作来说,编码的习惯比技术更加主要。工作中你面试的大部分技术都不需要用到的。工作中,因为你的编码习惯不好,写的代码质量差,代码冗余重复多,很多无关的代码和业务代码搅在一起,导致了你疲于奔命应付各种问题。 所以我作为SE,不管接手任何项目组,第一步就是制定代码框架,制定项目组的开发规范,把代码量减下去。事实上证明,这一步之后,大家的代码量能下去最少1/3,后台的问题数下降比较明显,大家的加班会比之前少。 这就是我说的工作中,编码习惯(或者说编码风格)比技术更加重要。