3.5 内聚和耦合
设计良好的C#程序集中的代码应恰当分组,即所谓的高内聚。若将不属于同一类型的代码划归在一组则会产生低内聚的代码。
我们希望类尽可能独立。一个类对另一个类依赖性越强,它们的耦合度就越高,即紧耦合。而相互独立的类越多,聚合度就越低,即低内聚。
因此,定义良好的类应当高内聚低耦合。以下例子分别展示了紧耦合与低耦合的代码:
3.5.1 紧耦合范例
在以下例子中,TightCouplingA
类的成员变量_name
可以直接从外界访问,这破坏了封装。事实上,_name
变量应当为私有变量并只能够由类中的属性方法更改。虽然Name
属性提供了get
和set
方法对_name
变量进行验证,但如果不调用属性就可以跳过这些检查,那么属性就变得毫无意义。
另一方面,在以下代码中,TightCouplingB
类创建了TightCouplingA
类的一个实例。前者直接访问后者的_name
成员变量,并将其设置为null
,从而确定了彼此紧耦合的关系。此后更是将成员变量的值直接输出到了调试输出窗口:
接下来我们使用低耦合的方式重新实现上述范例。
3.5.2 低耦合范例
该范例中有两个类:LooseCouplingA
与LooseCouplingB
。其中,LooseCouplingA
声明了私有实例成员变量_name
,该变量通过公有属性赋值。
LooseCouplingB
创建了LooseCouplingA
的实例并对Name
属性进行取值赋值操作。由于无法直接访问_name
数据成员,因此取值赋值时均会对数据成员的值进行检查。
以上快速介绍了低耦合范例的内容。其中LooseCouplingA
和LooseCouplingB
的内容如以下代码所示:
LooseCouplingA
类将_name
声明为私有字段,因此它无法被外界直接修改,只能通过Name
属性间接访问:
LooseCouplingB
类无法直接访问LooseCouplingA
的_name
变量,只能通过属性更改变量的值。
通过上述介绍,我们已经了解了耦合,以及如何在实现中避免紧耦合的代码,编写低耦合的代码。接下来仍将使用范例介绍低内聚和高内聚的代码。
3.5.3 低内聚范例
职责多于一种的类称为低内聚类。请看以下范例:
上述类至少有三种职责:
- 连接到数据源/从数据源断开连接。
- 抽取并转换数据,为生成报告做准备。
- 生成并打印报告。
可见,上述类已经破坏了单一职责原则。接下来我们将其分割为三个类并实现单一职责原则。
3.5.4 高内聚范例
以下范例将LowCohesion
类分割为三个类:Connection
、DataProcessor
与Report-Generator
。它们均遵循单一职责原则。在分割结束后,代码将变得更加整洁。
以下类中将仅包含和数据源连接相关的方法:
该Connection
类是高内聚的。
以下范例中的DataProcessor
类包含两个方法。它们从数据源抽取数据并将其转换成报告中需要的形式:
同样,这个类也是高内聚的。
以下范例中,ReportGenerator
类中的方法仅仅与生成和输出报告相关:
这个类也是高内聚的。
上述三个类均只包含那些与其职责相关的方法,因此它们都是高内聚的。
接下来我们将介绍如何在代码设计中使用接口代替类,以便使用依赖注入或控制反转的方式向构造器和方法中注入代码。