1.3 复杂性来源
在您开始编写代码之前,当您为自己准备另一杯咖啡时,我想挑战一下您的设计。它在纸上可能看起来漂亮而清晰,但我要说这个设计太复杂了。
这不是因为您选择了错误的类,也不是因为您误解了类之间的关系。它要深得多。这是关于您选择实现系统的编程范例。它是关于面向对象范例的。它是关于面向对象的趋势会增加系统的复杂性。
TIP OO有创建复杂系统的趋势。
正如我们在第0章中提到的,我所指的复杂性类型使系统变得难以理解,因为它在美丽的论文Out of the Tar Pit中有定义。它与程序消耗的资源的复杂性无关。
同样,当我提到简单性时,我的意思是不复杂,换句话说:容易理解。
请记住,复杂性和简单性(就像困难和容易一样)不是绝对的,而是相对的概念。我们可以比较两个系统的复杂性,认为系统A比系统B更复杂(或更简单)。
NOTE 本书上下文中的复杂意思是:难以理解
正如我们在本章的介绍中所提到的,在面向对象中有很多方法可以降低复杂性。这本书的目的不是批评面向对象。其目的是提出一种称为面向数据编程(DO)的编程范例,它倾向于构建不那么复杂的系统。事实上,DO范例与OO是兼容的,如果选择构建一个遵循DO原则的OO系统,系统将不会那么复杂。
TIP DO与OO兼容。
根据DO的说法,您的系统以及许多面向对象系统的复杂性的主要来源是:
- 代码和数据混合
- 对象是可变的
- 数据作为成员锁定在对象中
- 代码作为方法锁定在类中
在本章的其余部分中,我们将在图书馆管理系统的上下文中说明上述每个方面(在表1.1中总结),并解释它在何种意义上是复杂性的来源。
表1.1面向对象编程的各个方面及其对增加的系统复杂性的影响
方面 | 对复杂性增加的影响 |
---|---|
代码和数据混合 | 类往往涉及许多关系。 |
对象是可变的 | 阅读代码时的额外思考 |
对象是可变的 | 多线程环境下的明确同步化 |
数据作为成员锁定在对象中 | 数据序列化并不简单 |
代码被锁定在类中 | 类的层次结构很复杂 |
1.4 当代码和数据混合在一起时,类往往涉及到许多关系
评估类图复杂性的一种方法是只看实体和它们的关系(忽略成员和方法),如图0.2所示。
当我们设计系统时,我们必须定义不同代码和数据之间的关系:这是不可避免的。
TIP 在面向对象中,代码和数据混合在类中,数据作为成员,代码作为方法。
从系统分析的角度来看,代码和数据混合在一起的事实使系统变得复杂,因为实体往往涉及许多关系。
在图1.11中,我们仔细查看了Member类。成员涉及5个关系:2个数据关系和3个代码关系。
数据关系:
- 图书馆有很多会员
- 会员有很多图书借阅
代码关系:
- 成员扩展用户
- 图书馆管理员使用会员
- 会员使用BookItem
设想一下,我们能够以某种方式将成员类拆分成两个独立的实体:
- 代码的MemberCode
- 数据的MemberCode
我们将看到图1.12所示的图,而不是具有5个关系的成员类,其中包括:
- 具有3个关系的MemberCode实体
- 具有2个关系的MemberData实体
将Member拆分为MemberCode和MemberData的类关系图由两个互不相连的部分组成,其中每个部分都比原始关系图更容易理解。
现在,让我们将原始类图的每个类拆分为代码实体和数据实体。结果图如图0.3所示:现在,系统由两个断开的部分组成:
- 只涉及代码实体的部分
- 只涉及数据实体的部件
TIP 一种系统,其中每个类都被分成代码,数据由两个互不相连的部分组成,其中每个部分都比代码和数据混合的系统简单。
由此产生的系统-由两个互不相连的子系统组成-比原始系统更容易理解。这两个子系统是断开的,这意味着每个子系统都可以单独理解。我们可以先了解系统的数据部分,然后再了解系统的代码部分(或者相反)。
由此产生的系统并非意外变得更简单,它是将代码与数据分离的逻辑结果。
TIP 由不相连的简单部件组成的系统比由单个复杂部件组成的系统要简单。