【架构设计】你的类足够“专一”吗
前言
软件设计 SOLID 原则中有一个最基础的原则就是单一职责原则,我想绝大部分的程序员都知道,而且都理解它的意思,甚至觉得很简单。但是往往“看懂”和“会用”是两回事,而“用好”更是难上加难。好比我们项目,一开始一直和大家强调类的单一职责,随着业务不断发展,不同的同事都往这个类“添砖加瓦”,最终导致一个类 5000 多行,如下图所示,连 IDE 都受不了,变得卡顿,可想后面维护成本有多大了,究竟为什么会这样呢?又该如何解决呢?
欢迎关注微信公众号「JAVA 旭阳」交流和学习
什么是单一职责原则(SRP)?
单一职责原则的英文是 Single Responsibility Principle
,缩写为 SRP
。这个原则的英文描述是这样的:A class or module should have a single reponsibility
。如果我们把它翻译成中文,那就是:一个类或者模块只负责完成一个职责(或者功能)。简单来说,一个类只负责完成一个职责或者功能。也就是说,不要设计大而全的类,要设计粒度小、功能单一的类。
上例子,比如,一个类里既包含订单的一些操作,又包含用户的一些操作。而订单和用户是两个独立的业务领域模型,我们将两个不相干的功能放到同一个类中,那就违反了单一职责原则。为了满足单一职责原则,我们需要将这个类拆分成两个粒度更细、功能更加单一的两个类:订单类和用户类。
如何判断类是否够“专一”?
听起来类的单一职责很简单,很容易区分清楚,就比如上面的例子中的用户和订单,那为什么我们在开发过程中还是容易把一个类写的越来越大呢?好吧我们项目中的这个 5000 多行的类,实际上都是和“指标”这个领域模型的相关操作,所以对于大部分同事很难去界定出来哪些功能归为一类,属于一个职责范围之内的,既然搞不清楚,那么我就都写一起好了,大部分都是这样去写代码的。
这边再用一个实际的例子解释下这种模糊的情况。
比如下面的UserInfo
类的设计是否满足单一职责原则呢?
有人认为,
UserInfo
类包含的信息都是用户相关的行为,满足单一职责原则。有人认为,地址信息在
UserInfo
类中,所占的比重比较高,可以继续拆分成独立的UserAddress
类,UserInfo
只保留除Address
之外的其他信息,拆分之后的两个类的职责更加单一。
其实是否满足单一职责原则,需要结合具体的业务场景,如果你现在是社交场景中,地址单纯用来展示使用,那么它就是符合单一职责原则。如果在电商场景中,地址就要订单、物流等信息中出现,那么就要做进一步拆分。
所以说,不同的应用场景、不同阶段的需求背景下,对同一个类的职责是否单一的判定,可能都是不一样的。在某种应用场景或者当下的需求背景下,一个类的设计可能已经满足单一职责原则了,但如果换个应用场景或着在未来的某个需求背景下,可能就不满足了,需要继续拆分成粒度更细的类。
小结:
评价一个类的职责是否"专一",我们并没有一个非常明确的、可以量化的标准,可以说,这是件非常主观、仁者见仁智者见智的事情。实际上,在真正的软件开发中,我们也没必要过于未雨绸缪,过度设计。所以,我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类,进行持续重构。
该具体怎么做?
上面判定一个类是否具备单一职责,还是比较模糊、主观,那么有没有具体地、可执行的标准来判断呢?比如以我们的项目为例:
最关键的一条,如果类中的代码行数、函数或属性过多,影响到代码的可读性和可维护性,就需要考虑对类进行拆分。我们项目后面要求代码不能超过 1500 行,函数最好不要超过 20 个。
私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
类中大量的方法都是集中操作类中的某几个属性,比如,在
UserInfo
例子中,如果一半的方法都是在操作address
信息,那就可以考虑将这几个属性和对应的方法拆分出来。另外一个很关键的一点是,在为了一个加方法、属性的时候,一定要想清楚这是不是这个属于这个类职责,不清楚,就问问团队其他人,慢慢培养自己的“专业感”,千万不要有无脑地放哪里都行、实现功能就行的心态。
总结
本文探讨了类的单一职责原则,如何判定一个类是否"专一",以及具体该如何做。不仅仅是类,我们可以进行引申,你的方法足够单一吗?你的模块或者服务足够单一吗?另外,很关键的一点就是你在写代码的时候,一定要动脑子,思考这段代码写这里合适吗?,为这段代码找到一个真正属于它的“家”。多多思考,那么你一定会变得越来越专业。
欢迎关注微信公众号「JAVA 旭阳」交流和学习
更多学习资料请移步:程序员成神之路
评论