如何在代码设计中实现职责分离?

在面向对象编程中,你是不是经常听到“要实现代码间的职责分离”,但是具体什么样的代码才算得上是清晰的职责分离,似乎却又总是模糊不清。比如:

  • 代码模块越多职责越清晰?

  • 按照需求来分配职责就是职责分离?

  • 模块化就是职责分离?

实际上,要想写出“职责分离”的代码,单从字面含义是很难下手的,因为业界并没有统一的通用标准。比如,什么是职责分离?为什么职责分离很重要?具体如何实现职责分离?

所以,今天我们就一起带着这些问题来学习与理解职责分离。

一、高内聚、低耦合:职责分离的目标

什么是职责?在《敏捷软件开发:原则、模式与实践》这本书中,把“职责”定义为“变化的原因”。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责。

那什么是职责分离?为了更好地理解职责分离,我们先从一个熟悉而又陌生的概念讲起,那就是内聚。

这里我们结合一个例子来说明一下,如下图所示:


在该图中,模块按照相对小的功能进行划分(数字表示,比如模块 1),这里我们假设业务领域已经被分析为有三个不同的功能,并放在了一个模块内(叫“我的模块”),其中,模块 A、B、C 之间没有什么共同的职责,分别在独立的数据上运行。

你有没有一种有种曾相似的感觉?没错,常见的 Controller+Service+Dao 里的各种功能多是这样的组织形式,看上去很漂亮的结构,但实际上却是最混乱的,俗称大泥球结构,这也是内聚度很低的一种模式。

观察上面的关系图你会发现,八个模块都依赖着“我的模块”。在这种情况下,如果想要在系统中的其他模块使用功能 A、B 或 C,那么调用就会依赖整个“我的模块”,这显然导致了太多的依赖,大大降低了可维护性。

那么,为了提高内聚性,我们就应该对功能进行分离,如下图所示:

很明显,现在每个模块的依赖比原来少了很多,模块 A、B、C 之间没有直接的关系,并且模块 3 是唯一一个依赖模块 A 和模块 C 的模块。这样带来的好处是,当我们依赖 A 或 B 或 C 时,能够清晰地知道它们依赖了哪些模块,也就是下次修改代码时影响的模块有哪些,将变更风险控制在有限范围内。这样才算是做到了真正的高内聚,也就是各个模块专注于自己最重要的某个职责,并建立起与其他模块之间清晰的界限。

所以说,内聚本质上表示的是系统内部的各个部分对同一个问题的专注程度,以及这些部分彼此之间联系的紧密性

你可能也注意到,对同一个问题的专注程度才是判断内聚高低的标准,而职责分离只是实现高内聚的一种方法而已。

那么,现在我们就可以来回答“什么是职责分离”这个问题了。简单来说,职责分离就是将不同变化原因引起的类或方法修改行为拆分到不同类或方法里去

二、职责分离的重要性

那为什么职责分离很重要呢?主要有三点原因。

第一点,直接对问题进行对象建模,方便厘清构建逻辑。在面向对象编程中,通常都会建议你将现实中的事物或问题翻译成对象,这样更“拟人化”,也能更好地进行编码实现,就好比让每一个代码模块能够像人一样具备自己的属性和行为,只需要指定特定的职责就能让各自模块运行良好,而不是像面向过程编程那样把所有的功能都放在一起。比如,针对商品属性相关的问题,我们可以建立商品基本信息对象、赠品信息对象、活动商品对象等各类对象,然后通过不同的职责关联统一起来,这样在修改时就能通过清晰的职责边界来理解代码逻辑关系了。

第二点,将问题分解为各种职责,更有利于系统的测试、调试和维护。比如,开发一个电商系统,你一定不会把所有的系统(订单、物流、商品、支付)都放在一起,因为这样不仅不利于理解系统,而且其中任何一个子系统的代码修改都会影响到别的系统。除此之外,职责分离不够的系统,测试起来也会非常痛苦,因为每一次的修改不敢保证不会影响别的系统,那么就需要测试相关联的系统,这样大大降低了交付效率,同时还会因为测试不充分而出现线上问题。

第三点,提高系统的可扩展性。虽然可扩展性是现代软件设计的必选项之一,但是很多系统在前期时间紧、任务重的情况下几乎都会放弃一部分扩展性,于是矛盾就出现了,系统已经上线,但用户需求却不断变化,这时如果需要添加一些新东西,那么你就需要改动所有没有清晰划分职责的地方,这样势必影响系统的运行。但如果做好了职责划分,那么你就只需要改动具有相应职责的类,而不会影响到系统的其他部分,这样不仅能提高系统的可扩展性,还降低了代码修改引入风险的概率。

三、职责分离的时机

在编码实现中,职责分离的时机大致有三个:

  • 命名太过于笼统;

  • 改动代码后的测试规模很大;

  • 类和方法过大。

首先,命名太过于笼统时是职责分离的好时机。 你一定希望类、方法和其他对象(包、服务等)的命名能够直接反映出它们的作用,同时还要足够简短,方便记忆。如果命名过长或表述模糊,通常可能是因为类或方法包含的职责过多而无法筛选出职责的优先级,这样的类随着修改越多,问题出现的概率也越高。这时就是进行职责分离的好时机,通过将不同职责拆分出来,就能很好地限定问题范围,即便出现问题需要修改也只是在限定范围内修改,不会影响到其他模块。

其次,如果每次修改代码都要重新进行一次全量测试,那么这也是进行职责分离的好时机。“修改一处影响全部”可以说是开发和测试都不愿意面对的情况之一,这说明代码耦合性高、内聚度低。换句话说就是,代码中的职责过多,彼此之间相互影响。这时可以通过修改代码处的职责来进行代码重构,找出合适的职责进行分离,逐步减小全量测试的范围,这样就能减少职责之间的相互干扰。

最后,遇见超大类或方法也是一个时机。 绝大多数情况下,超大的类或方法都是职责划分不清导致的代码过度耦合(当然,有的算法实现本身就很复杂,进而导致出现超长的方法,这种情况不在这次的讨论范围内)。比如,当一个类包含了太多的其他类时,可以用一个简单的原则来判断职责是否过多,那就是:能否拆分出来更多的子类?如果不能,那么这个类很可能就是高内聚的,职责比较单一;如果能,那么这个类还不够内聚,职责还有多余的。

四、如何通过职责分离实现高内聚

职责分离更多的是一种设计思想和编程技巧,主要的理念就是将模糊笼统的问题拆分为

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱娃哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值