前置知识
- 有项目编写经历
- 听说过设计模式
前言
我们经常有听过设计模式,也明白设计模式对项目和代码设计逻辑的重要性。但是你是否知道,所有的设计模式,无论是经典的23种设计模式之内,或者23种之外的,其实他们都是基于最基本的6种设计原则展开的。理解6种设计原则,我们可以从根源更好的理解23种经典的设计模式。甚至待到你的经验炉火纯青之时,可以创造出自己的一种设计模式。
本文带大家学习和了解第一种设计原则,单一职责原则
单一职责原则
定义:就一个类而言,应该仅有一个引起它变化的原因。应该只有一个职责。 每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。
上文的定义源自百度,其意思简单来说为:一个类或者模块只承担一个职责
单一职责指的是到哪个程度的单一?
单一职责原则实际上很好理解,但是其边界我们却难以判定。
那么,一个类完成一个业务功能是单一职责,还是一个类完成一个业务功能中某个流程步骤的功能称之为单一职责?
同时单一职责描述的是类和模块,那么类可以被称之为模块吗?还是说多个类才能祖成一个模块?我们实际中的单一职责应该针对某个类,还是针对多个类组成的模块呢?
要回答上述的这些问题,需要我们对单一职责有更加深的理解。
事实上,对于 单一职责原则 这种较为难以界定实际边界的规则,我们对其的定义应该是随着业务的变化而变化的,其定义应该是动态化的。
当业务需求小的时候,我们认为模块是类的抽象化表达,那么一个类能完成的一个功能块,我们也称之为模块。那我们的单一职责自然也是一个类而已。
当业务需求变大了,我们抽取出多个类一起完成某个功能,那我们认为这个功能涉及到的类合起来实现的为一个模块。但是这个时候,单一职责并非就全指整个模块了,对于整个模块,单一职责的评判是该模块是否实现的是单一的一个大功能,没有去实现其他的功能。对于其中的类,我们需要看是否包含其他业务领域的操作,如果包含,且存在该领域的业务,那么我们应该把这些关于其他业务领域的代码拆解出来,成为一个新的类;而如果包含,但目前不存在该领域的业务,那这个类也可认为是足够单一的类了。
举个例子:
某个功能类中包含订单和地址的信息和操作
但是目前并不需要关于订单或者地址的功能,那么该功能类可认为是符合单一职责原则了
若是目前有涉及订单和地址的业务,我们就需要把订单和地址单独出来成为一个类,这样子才算符合单一职责
所以说,单一职责的范围,取决于我们的业务范围
所以我们代码是否单一,都是基于当前的业务来说的,我们要维护代码符合该原则,就需要不断地持续重构
我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构
那么基于当前编程界的实际经验,我们有哪些清晰的原则判断是否符合单一原则呢?
我们可以从以下方面判断
类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分;
类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的 Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;
类中大量的方法都是集中操作类中的某几个属性,比如,在 UserInfo 例子中,如果一半的方法都是在操作 address 信息,那就可以考虑将这几个属性和对应的方法拆分出来。
我们设计的类越单一就一定越好吗?
实际上并非如此,单一的范围是有限度的,我们将单一职责的规定过于细化的时候,想让每一个小功能都更单一时候,会适得其反,导致代码易用性遭到破坏。
我们设计的时候,把一些其中功能相关性较大的类,为了单一职责,而把其拆开为多个类。反而导致代码内聚性和可维护性减低。
所以说,我们是否要设置的更加单一,还是要以实际的操作和功能来判断
不管是应用设计原则还是设计模式,最终的目的还是提高代码的可读性、可扩展性、复用性、可维护性等。我们在考虑应用某一个设计原则是否合理的时候,也可以以此作为最终的考量标准
单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性。同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。但是,如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性