3.5 内聚和耦合

设计良好的C#程序集中的代码应恰当分组,即所谓的高内聚。若将不属于同一类型的代码划归在一组则会产生低内聚的代码。

我们希望类尽可能独立。一个类对另一个类依赖性越强,它们的耦合度就越高,即紧耦合。而相互独立的类越多,聚合度就越低,即低内聚

因此,定义良好的类应当高内聚低耦合。以下例子分别展示了紧耦合与低耦合的代码:

3.5.1 紧耦合范例

在以下例子中,TightCouplingA类的成员变量_name可以直接从外界访问,这破坏了封装。事实上,_name变量应当为私有变量并只能够由类中的属性方法更改。虽然Name属性提供了getset方法对_name变量进行验证,但如果不调用属性就可以跳过这些检查,那么属性就变得毫无意义。

另一方面,在以下代码中,TightCouplingB类创建了TightCouplingA类的一个实例。前者直接访问后者的_name成员变量,并将其设置为null,从而确定了彼此紧耦合的关系。此后更是将成员变量的值直接输出到了调试输出窗口:

接下来我们使用低耦合的方式重新实现上述范例。

3.5.2 低耦合范例

该范例中有两个类:LooseCouplingALooseCouplingB。其中,LooseCouplingA声明了私有实例成员变量_name,该变量通过公有属性赋值。

LooseCouplingB创建了LooseCouplingA的实例并对Name属性进行取值赋值操作。由于无法直接访问_name数据成员,因此取值赋值时均会对数据成员的值进行检查。

以上快速介绍了低耦合范例的内容。其中LooseCouplingALooseCouplingB的内容如以下代码所示:

LooseCouplingA类将_name声明为私有字段,因此它无法被外界直接修改,只能通过Name属性间接访问:

LooseCouplingB类无法直接访问LooseCouplingA_name变量,只能通过属性更改变量的值。

通过上述介绍,我们已经了解了耦合,以及如何在实现中避免紧耦合的代码,编写低耦合的代码。接下来仍将使用范例介绍低内聚和高内聚的代码。

3.5.3 低内聚范例

职责多于一种的类称为低内聚类。请看以下范例:

上述类至少有三种职责:

  • 连接到数据源/从数据源断开连接。
  • 抽取并转换数据,为生成报告做准备。
  • 生成并打印报告。

可见,上述类已经破坏了单一职责原则。接下来我们将其分割为三个类并实现单一职责原则。

3.5.4 高内聚范例

以下范例将LowCohesion类分割为三个类:ConnectionDataProcessorReport-Generator。它们均遵循单一职责原则。在分割结束后,代码将变得更加整洁。

以下类中将仅包含和数据源连接相关的方法:

Connection类是高内聚的。

以下范例中的DataProcessor类包含两个方法。它们从数据源抽取数据并将其转换成报告中需要的形式:

同样,这个类也是高内聚的。

以下范例中,ReportGenerator类中的方法仅仅与生成和输出报告相关:

这个类也是高内聚的。

上述三个类均只包含那些与其职责相关的方法,因此它们都是高内聚的。

接下来我们将介绍如何在代码设计中使用接口代替类,以便使用依赖注入或控制反转的方式向构造器和方法中注入代码。