编程规则 - 3 类设计规则 (2) 类设计的基本要求

3.3 类设计的基本要求

上面讲述了系统设计和类设计的基本规则,理论性较强,你也许一段时间难以理解好,下面介绍类设计的基本要求,更加易于实践,你需要认真做好,上面的那些规则你可以在实践过程中逐步理解,软件设计是一门实践科学,纸上谈兵永远难以得其门而入,实践是最好的老师,不过你还是要努力体会上面原则和方法,它会让你少走弯路。简单地说设计类时的基本要求是:

1) 类要短小、职责单一(SRP单一职责原则)

2) 类要高内聚低耦合

3) 类及类之间的关系要有合理的结构化体系。

这几条看似很简单,但由于概念抽象,还是不易于理解和把握,下面会分别说明,在说明之前,先给出较通俗的类设计基本要求:

a) 类要描述客观业务领域和解决方案(技术领域)的真实对象;

b) 类要有总体架构体系;

c) 类要有一个简洁的名称;

d) 类要尽可能隐藏内部信息;

说明:a)告诉我们不要按照我们的臆想设计类,不要创造怪物(软件工程中传说的人狼就是这样制造出来的啊),面向对象不仅仅是OO技术,更重要的是你在设计时要面向客观的业务领域对象,每个类都必须与实际业务领域和技术领域的客观事物对应,如果找不到对应的事物,你就要注意了,你可能在创造怪物。你可能会说我需要这个类,但是的确找不到对应的事物;这时你需要换个思路,事实上你需要把业务领域想象为一个全手工作业的系统,这时需要的事物在这个领域中就都出现了,其实就是将业务活动打回到原始手工状态(没有计算系统,纯手工系统如何运作的状态),例如纯手工作业的银行系统,银行前台会有接柜的柜员,后台会有会计、记账员,他们要使用各种凭证、账本、台账和登记簿,这些凭证、账簿等就是我们提炼实体对象的客观事物,各种“员”,就是我们提炼操作、服务类对象对应的客观事物;如果这种手工银行准备实现通存通兑,就需要有一个集中的后台部门,前端柜员需要通过电话或传真,把客户要办的业务信息发到后台,后台根据集中的账簿进行处理(实际上早期的银行的确如此处理业务),这时就引出了通讯、报文等事物,也就更接近目前C/S(或B/S)模式的业务系统了,解决方案领域的事物就出来了。这时你可能就找到了面向对象真正含义,并为你的类找的了对应的可观事物,如果你还没找到,你就要检讨了,极有可能你在制造怪物。

模拟手工往往是对软件系统设计的贬义说法,但是两个方面:其一,客观业务领域是通过几十年业务发展形成的,积累了人类的智慧,理应作为我们系统设计的重要参考。其二,我们在设计时也不能盲目机械地模仿,而必须进行分析、抽象,综合、提炼去伪存真,你就会发现—对象就在现实中,真理可观存在,等待你智慧的大脑去发现

b)在做类设计时,除了做好一个具体类的设计,还必须做好与类相关的架构设计:

包结构设计,包是类的容器,一个系统通常会有若干类构成,这些类如何合理地组织起来,有一个清晰的组织结构是非常重要的,必须重视包结构的设计。

类的继承结构设计,类实际上是生存在一个环境之中,仅注重类自身的设计还远远不够,必须重视类的继承关系及相互关系(下面会介绍),我们许多设计,都把类设计成一个独立体,天下独尊,按着a)中的原则,类是要与客观事物对应的,而客观世界中是没有这种独生独长的事物的,石头里蹦出来的猴子只是神话中的故事,因此,我们的类设计必须杜绝不实现任何接口也不继承任何类的设计,除非你本身就是接口或基类,同时基类和接口必须由总体组设计。

事实上,类主要划分为两大类,数据类(如实体对象、传输对象等,相当于java Bean)和行为类(如前端的AEM、后台的服务),在我们的开发体系下,已经分别确定了他们的顶级接口和基类,我们设计的类必须遵循此继承体系。尽管如此,在此体系之下的具体类继承体系的设计更为重要,涉及到你对一个业务领域的认识水平,例如我们在设计银行核心系统的传输对象时,如何将银行上千个功能对传输对象的要求,分解为几大类,各大类下再如何划分为若干小类,从而用一套结构合理、小规模的类满足银行全部功能对数据传输的细节要求,对于同一种类的业务通过基类的拓展,实现所有相关子类的拓展,底层子类的修改又不影响其他类,达到稳定与拓展的均衡。

类的依赖层次,尽管在系统设计过程中,我们不断强调解耦!解耦,但如果一个类与任何其他类都无关联,那么该类也就没用了,也就是说类必然会与其他类相互作用和依赖,必然要耦合,问题的关键在于如果将耦合降到最低,做到必须耦合的才耦合,依据最小知识原则和依赖倒置原则,一个类尽可能依赖最少的类,在与非本包内的类耦合时,要依赖接口、基类或抽象类,不要依赖非本包的具体类。下图给出了一个类依赖关系示意图:

两个包之间通过接口依赖,更进一步可以通过放在公共包中的工厂方法获取B包中的实际对象,同一个包内的类之间的依赖关系要有合理的层次,合理的依赖层次能够降低类的耦合度,要做到这样,类设计要遵循单一职责原则和最小知识原则,不与陌生人说话,只和朋友圈打交道。这样就可以将类与类之间的影响降低到最小,从而利于系统的拓展和稳定。

c)类要有一个简洁的名称,类的名称要能够很好地、准确地、简明地描述其职责,这也是坚持一个类是否职责过多的好办法,如果你没办法为类起一个简单、准确的名字,可能意味着:①这个类职责太多;②这个类职责不清;③你还没有为它确定一个好名字;如果是最后一点,你可以先为它起一个朴实的名字,简明地表达它的职责就好,记住当有了更恰当的名字是,要积极地重构,一个恰当准确的名字对于类非常重要,名如其类、类如其名啊。

类名应该是名称或名词短语,不应该是动词。

d)类要尽可能隐藏内部信息;我们规定类的属性必须私有化,尽可能不对外提供设置方法(数据对象和传输对象除外),类的方法不需要公开的就一定不要公开,内部使用数据模型(如List、Set、Map等)不允许对外暴漏。

下面再对前面1)-3)原则说明如下:

1) 类要短小、职责单一(SRP单一职责原则)

短小不是目标,职责单一而完备才是关键,事实上,世界上一切事物的进化过程就是一个分工越来越单一明确的过程,以生物进化为例,最原始的生命只是一堆蛋白质簇拥而成,没有分工、组织松散,因此也非常脆弱,后来逐步进化,一部分负责行走,一部分负责进食,一部分负责消化,一部分负责运输,逐步就形成组织和器官,逐步走向生命的高级形态;同样软件系统也是一个无形的生命体,没有严格分工、组织松散混乱的系统必然是一个原始的软件体,有着良好体系结构、分工明确具体的软件系统才是一个处于高级形态的软件体。类作为构成软件体系的基本单元,它必须职能单一而完备,要遵循SRP原则。

2) 类要高内聚低耦合

如果类满足了单一职责原则,它往往就高内聚了,因为它仅围绕单一职责工作,专注自然就内聚,一个类首先它的属性必须相对少,所有的属性都是它必须拥有的,剔除与其职能无关的属性;其考察一个类的内聚度,可以通过考察它的方法与它的属性之间的紧密程度,如果每个方法都使用到多个属性,表示它的内聚度是比较高的,反之内聚度是比较低的,内聚度高表明类的属性和方法相互依赖、相互结合成为一个逻辑整体,专注于一个职责。

低耦合,要参照类的依赖层次的说明处理好类的依赖关系。

3) 类及类之间的关系要有合理的结构化体系。

参见上面关于b)的说明。

你可能感兴趣的:(编程规则 - 3 类设计规则 (2) 类设计的基本要求)