C# 中的结构型设计模式

2025年03月17日 | 阅读 9 分钟

C# 中的结构型设计模式是什么?

根据维基百科,结构型设计模式通过指出实现事物之间关系的简单方法来简化软件工程。简而言之,结构型设计模式主要用于管理接口和类结构以及类之间的关系。

何时使用?

在实时应用程序中,有时需要修改类的结构或其类之间的关系,但我们不希望项目受到这些更改的干扰。此外,通过在 User 类中使用 Product 类,可以在 Product 和 User 之间建立一对多关系。

这两个类的排列或它们的关系明天会改变。在这种情况下,结构型设计模式就派上用场了。

什么是适配器设计模式?

一种名为适配器设计的结构型设计允许具有冲突接口的对象进行协作。当您希望使用现有类,但它们的接口需要与您的需求匹配时,此技术会很有帮助。

适配器设计模式充当两个不兼容事物之间的链接。假设 A 是第一个对象,B 是第二个对象。对象 A 想要使用对象 B 的部分服务。在这些情况下,适配器将介入并充当对象 A 和 B 之间的中介或桥梁。对象 A 现在将调用适配器,适配器将在完成任何所需的修改或转换后调用对象 B。

示例

接下来,我们将把适配器设计模式的 UML 图与我们的示例进行比较。请查看下面的图片。这里可以看到两个系统,或者更确切地说,两个接口。右侧是第三方计费系统,左侧是客户端,即当前 HR 系统。我们现在将研究这两个系统之间的不兼容性,以及我们如何使用 C# 的适配器设计模式使它们兼容。

Structural Design Patterns in C#

如您所见,ProcessSalary 是第三方计费系统提供的一项功能。员工列表或 List 将是此 ProcessSalary 方法的输入参数。然后,它将遍历每位员工,计算其工资,并将钱存入其银行账户。

员工数据在左侧或当前 HR 系统中存储为字符串数组。HR 系统希望处理员工工资处理。然后,HR 系统必须调用第三方计费系统的 ProcessSalary 方法。

但是,如果您查看 HR 系统,您会发现员工数据存储在字符串数组中,而第三方计费系统的 ProcessSalary 方法要求数据采用 List 格式。由于 List 和字符串数组之间不兼容,HR 系统无法直接调用第三方计费系统。因此,这两个系统无法共存。

我们如何才能让它们协同工作?

使用 C# 适配器设计模式,这两个系统或接口可以协同工作。如下图所示,我们必须在第三方计费系统和 HR 系统之间添加一个适配器。

Structural Design Patterns in C#

HR 系统现在将员工数据作为字符串数组发送到适配器。此适配器将从字符串数组中读取数据,填充员工对象并将每个员工对象添加到 List 集合中。随后,适配器将把 List 转发给第三方计费系统的 ProcessSalary 函数。然后,使用 ProcessSalary 技术确定每位员工的工资,并将其存入员工的银行账户。

因此,我们可以使用 C# 中的适配器设计模式使两个不兼容的接口协同工作。再次强调,有两种方法可以在 C# 中实现适配器设计模式。

实施

现在,让我们继续研究如何在 C# 中实现对象适配器设计模式。

步骤 1:创建 Employee 类

以下代码应复制并粘贴到名为 Employee.cs 的类文件中。适配器和第三方计费系统(即 Adaptee)都将使用此文件。在这里,我们首先使用必要的参数构造了 Employee,然后使用类构造函数初始化了属性。

步骤 2:创建 Adaptee

客户需要的功能将包含在此类中。然而,此接口需要更客户端兼容。为了使用它,创建一个名为 ThirdPartyBillingSystem.cs 的类文件,并插入以下代码。此类中的 ProcessSalary 方法在接收到员工列表作为输入参数后,处理每位员工的工资。

步骤 3:创建 ITarget 接口

换句话说,创建一个名为 ITarget.cs 的接口,并将以下代码粘贴到其中。此类定义了抽象的 ProcessCompanySalary 方法,适配器将实现此方法。再次强调,客户端将使用此方法处理工资。

步骤 4:创建适配器

因此,创建一个名为 EmployeeAdapter.cs 的类文件,并将以下代码粘贴到其中。此类提供 ProcessCompanySalary 方法的实现以及 ITarget 接口。此外,此引用了 ThirdPartyBillingSystem (Adaptee) 对象。ProcessCompanySalary 方法首先以字符串数组的形式接收员工信息,并将其转换为员工列表。然后,员工列表作为参数传递给 ThirdPartyBillingSystem (Adaptee) 对象上的 ProcessSalary 方法。

步骤 5:客户端

我们的 HR 系统,或者程序类的 main 方法,将是这种情况下的客户。请如下所示调整 Main 方法。您会发现员工数据当前存储在字符串数组中。接下来,我们创建一个 EmployeeAdapter 对象,并使用字符串数组作为参数调用 ProcessCompanySalary 函数。因此,第三方计费系统和客户端现在在适配器(也称为 EmployeeAdapter 对象)的帮助下进行协作。

UML 图

请参考下图以理解类或 UML 图以及对象适配器设计模式的许多元素。如您所见,客户端使用 ITarget 接口创建适配器实例,然后使用该实例与 Adaptee 交互。适配器是允许使用两个不同、不兼容接口的元素。

Structural Design Patterns in C#

适配器设计模式有四个部分。它们按以下顺序排列

客户端:实现 ITarget 接口的类,或适配器(在我们的示例中是 EmployeeAdapter),是客户端类唯一能够看到的。

ITarget:适配器需要实现此特定接口。只有此接口(即实现它的类)对客户端可见。

适配器:此类使两个不兼容的系统或接口能够协作。它实现了接口方法和 ITrager 接口。

Adaptee:因此,在客户端使用它之前,需要对其进行修改或转换。这意味着适配器将接收来自客户端的调用,执行必要的转换,然后调用 Adaptee。

以上所有内容都与 C# 对象适配器设计模式相关。让我们继续,研究如何在 C# 中使用类适配器设计模式来实现相同的功能。

理解 C# 中的类适配器设计模式

这是应用 C# 适配器设计模式的另一种方法。此方法中的适配器调用将从 Adaptee 类继承并实现 ITarget 接口。因此,适配器类现在将成为 Adaptee 类的子类。因此,由于 Adaptee 方法可以通过继承访问,因此可以直接使用它,而无需创建 Adaptee 引用变量。

Structural Design Patterns in C#

类图与对象适配器设计模式的类图相同。在对象适配器设计模式的实例中,它将使用适配器拥有的 Adaptee 对象的引用调用 Adaptee 方法。

实施

实现与对象适配器设计模式的实现相同。

EmployeeAdapter 类是唯一的区别。在进行任何必要的转换或讨论之后,我们必须调用 ProcessSalary 方法。

因此,为了使用 C# 类适配器设计模式,请如下所示编辑 EmployeeAdapter 类。ThirdPartyBillingSystem 类(在本例中为 Adaptee)是 EmployeeAdapter 类的来源,EmployeeAdapter 类实现了 ITarget 接口。

何时在 C# 中使用对象适配器模式和类适配器模式?

例如,您不能让继承与 Java 类一起工作;相反,您必须使用对象适配器设计模式使 Java 类与 Dot Net 类兼容。但是,如果允许继承,两个类都是同一个项目的一部分,并且它们是用相同的编程语言编写的,则应使用类适配器设计模式。

实时应用程序?

  • 重用现有代码:如果您已有提供所需功能的类,但它们的接口与您的系统使用的接口不同,则适配器可以弥补这一不足。
  • 为各种类开发相同的接口:当您希望使用相同的接口一致地处理多个类,但它们具有不同的接口时。
  • 支持多个数据源:这指的是您的应用程序需要统一处理来自多个源(例如数据库、文件系统和 Web 服务)的数据的情况。
  • 测试和模拟:当实际对象(例如数据库连接或外部服务)难以在测试环境中使用时,适配器可用于为单元测试构建存根或模拟。
  • 维护向后兼容性:适配器可用于使旧版 API 或数据模型与更新的应用程序或库兼容。
  • 跨平台兼容性:用于必须在支持多个环境或平台的同时保持应用程序代码一致性的情况。

让我们更详细地分析这些 C# 结构型设计模式

适配器模式:如果您希望在不更改源代码的情况下组合现有但具有不同接口的类,则适配器模式会派上用场。

在给定的示例中

Adaptee:当前具有不兼容接口的类称为 Adaptee。

此模式可用于在不更改代码的情况下在新环境中重用现有类,或将新组件集成到现有系统中。

组合模式

组合模式用于在树结构中表示对象,因为它使客户端能够一致地处理单个对象和对象组合。

在给定的示例中

Component:这充当组合的整个对象的接口。

Leaf:这代表组合的叶子对象,它们没有子对象。

Composite:这表示一个复合对象,它能够拥有子对象,并委托它们来实现它们的行为。

当您希望一致地处理单个对象和对象组,并且具有分层结构(例如文件系统或 GUI)时,此模式会派上用场。

装饰器模式

使用装饰器模式,您可以动态地向特定对象添加行为,同时保留同一类中其他对象的行为。它是添加新功能的子类化的一种多功能替代方案。

在给定的示例中

Component:这是可以附加职责的对象接口。

ConcreteComponent:这是可以应用其他职责的基对象的示例。

Decorator:装饰器的基础类,它包含对组件的引用,称为 Decorator。

ConcreteDecorator:这是具体装饰器类,它为 Component 赋予额外的职责。

当您希望添加或更改行为而不更改一系列类(每个类具有不同的职责)的基本实现时,此方法会派上用场。

结论

总之,C# 结构型设计模式提供了有效的方法来组织和控制代码库中类和对象之间的连接。在您的设计中包含这些模式使您的代码库更易于维护、更具可扩展性且更易于理解。它还有助于组织您的代码库。您可以利用这些原则创建更易于创建、管理和随时间调整的系统。无论是开发小型系统还是大型应用程序,理解和利用这些结构型设计模式都可以大大提高 C# 项目的质量和弹性。