Loading... > 大类一种表现形式就是我们上节课讲到的长函数,一个类只要有几个长函数,那它就肯定是一眼望不到边了; 还有一种表现形式,类里面有特别多的字段和函数。 类之所以巨大,大部分原因都是违反了单一职责原则。 记住:把类写小,越小越好。 # 一、分模块的程序 人类面对复杂事物给出的解决方案是分而治之。所以,我们看到几乎各种程序设计语言都有自己的模块划分方案,从最初的按照文件划分,到后来,使用面向对象方案按照类进行划分,本质上,它们都是一种模块划分的方式。这样,人们面对的就不再是细节,而是模块,模块的数量显然会比细节数量少,人们的理解成本就降低了。 实现某个功能时不知道该用哪个函数了,想用哪个函数翻半天都找不到了,只用到一个小功能要引入整个类(类中包含很多无关此功能实现的函数)的时候,**这就说明类的行数过多了**。 如果一个类里面的内容太多,它就会超过一个人的理解范畴,顾此失彼就在所难免了。 # 二、大类的产生 想要理解怎么拆分一个大类,我们需要知道,这些类是怎么变成这么大的。 ## 1、职责不单一 ```cpp class User { private: long userId; String name; String nickname; String email; String phoneNumber; AuthorType authorType; ReviewStatus authorReviewStatus; EditorType editorType; ... } ``` `面对这样一个类时,我们要问的第一个问题就是,这个类里的字段都是必需的吗?` 问题所在: 首先,普通的用户既不是作者,也不是编辑。作者和编辑这些相关的字段,对普通用户来说,都是没有意义的。其次,对于那些成为了作者的用户,编辑的信息意义也不大,因为作者是不能成为编辑的,反之亦然,编辑也不会成为作者,作者信息对成为编辑的用户也是没有意义的。 **在这个类的设计里面,总有一些信息对一部分人是没有意义,但这些信息对于另一部分人来说又是必需的。之所以会出现这样的状况,关键点就在于,这里只有“一个”用户类。** 普通用户、作者、编辑,这是三种不同角色,来自不同诉求的业务方关心的是不同的内容。只是因为它们都是这个系统的用户,就把它们都放到用户类里,造成的结果就是,任何业务方的需求变动,都会让这个类反复修改。这种做法实际上是违反了单一职责原则。 `单一职责原则`, 它让我们把模块的变化纳入考量。单一职责原则是衡量软件设计好坏的一把简单而有效的尺子,通常来说,**很多类之所以巨大,大部分原因都是违反了单一职责原则**。而想要破解“大类”的谜题,关键就是能够把不同的职责拆分开来。 解决办法: 虽然这是一个类,但其实,它把不同角色关心的东西都放在了一起,所以,它变得如此庞大。**我们只要把不同的信息拆分开来,问题也就迎刃而解了**。下面就是把不同角色拆分出来的结果: ```cpp class User { private: long userId; String name; String nickname; String email; String phoneNumber; ... } ``` ```cpp class Author { private: long userId; AuthorType authorType; ReviewStatus authorReviewStatus; ... } ``` ```cpp class Editor { private: long userId; EditorType editorType; ... } ``` 我们拆分出了 Author 和 Editor 两个类,把与作者和编辑相关的字段分别移到了这两个类里面。在这两个类里面`分别有一个 userId 字段,用以识别这个角色是和哪个用户相关`。这个大 User 类就这样被分解了。 Note: **评价一个类的职责是否足够单一,我们并没有一个非常明确的、可以量化的标准,因为随着需求的改变,就有可能发生变化**。 我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构 ## 2、字段未分组 > 大类的产生往往还有一个常见的原因,就是字段未分组。 比如,我们看一下上面拆分的结果,那个新的 User 类: ```cpp class User { private: long userId; String name; String nickname; String email; String phoneNumber; ... } ``` 即便相比于原来的 User 类小了许多,这个类依然也不算是一个小类,原因就是,这个类里面的`字段并不属于同一种类型的信息`。**比如,userId、name、nickname 几项,算是用户的基本信息,而 email、phoneNumber 这些则属于用户的联系方式**。 从需求上看,基本信息是那种一旦确定就不怎么会改变的内容,而联系方式则会根据实际情况调整,比如,绑定各种社交媒体的账号。所以,如果我们把这些信息都放到一个类里面,这个类的稳定程度就要差一些。所以,我们可以根据这个理解,**把 User 类的字段分个组,把不同的信息放到不同的类里面。** ```cpp class User { private: long userId; String name; String nickname; Contact contact; ... } ``` ```cpp class Contact { private: String email; String phoneNumber; ... } ``` 这里我们引入了一个 Contact 类(也就是联系方式),把 email 和 phoneNumber 放了进去,后面再有任何关于联系方式的调整就都可以放在这个类里面。经过这次调整,我们把不同的信息重新组合了一下,但每个类都比原来要小。 解决方法:前面是根据职责,拆分出了不同的实体,后面是将字段做了分组,用类把不同的信息分别做了封装。或许你已经发现了,**所谓的将大类拆解成小类,本质上在做的工作是一个设计工作。我们分解的依据其实是`单一职责`这个重要的设计原则**。 ## 3、类中成员的排列顺序 成员变量和函数之间: 在类中,成员变量排在函数的前面。成员变量之间或函数之间,都是按照“先静态(静态函数或静态成员变量)、后普通(非静态函数或非静态成员变量)”的方式来排列的。除此之外,成员变量之间或函数之间,还会按照作用域范围从大到小的顺序来排列,先写 public 成员变量或函数,然后是 protected 的,最后是 private 的。 类内部成员的排列顺序: 函数之间的排列顺序,会按照刚刚我们提到的作用域的大小来排列。实际上,还有另外一种排列习惯,那就是把有调用关系的函数放到一块。比如,一个 public 函数调用了另外一个 private 函数,那就把这两者放到一块。 # 三、错误的思想 如果我们把大类都拆成小类,类的数量就会增多? —>并不会 程序设计语言早就已经有了很好的解决方案,所以,我们会看到在各种程序设计语言中,有诸如包、命名空间之类的机制,将各种类组合在一起 最后修改:2025 年 07 月 01 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏