适配器模式(类适配器,对象适配器)

news/2025/1/7 19:29:49/

1. 适配器模式简介

适配器模式用于解决接口不兼容问题,将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以协同工作。适配器模式主要分为两类:

类适配器模式:通过继承适配者类来实现适配。
对象适配器模式:通过组合适配者对象来实现适配。

2. 定义接口

在 Interfaces 文件夹中定义目标接口(Target)和需要适配的接口(Adaptee):

 // IPlayer.cs - Target接口public interface IPlayer{void Attack();  // 玩家角色攻击void Move();    // 玩家角色移动}

 // Robot.cs - Adaptee类,来自第三方库,接口不兼容public class Robot{public void FireWeapon(){Console.WriteLine("机器人发射武器!");}public void Walk(){Console.WriteLine("机器人行走!");}}

2.1 定义并理解IPlayer接口和Robot类的功能

2.1.1. IPlayer 接口的功能

IPlayer 接口定义了玩家角色的基本行为,包括攻击和移动。具体方法如下:

  • Attack():表示玩家角色执行攻击动作。

  • Move():表示玩家角色执行移动动作。

IPlayer 接口是目标接口,客户端代码期望通过调用 Attack() 和 Move() 来控制玩家角色的行为。

2.1.2 Robot 类的功能

Robot 类是一个需要适配的类,它来自第三方库,提供了机器人角色的行为,但与 IPlayer 接口不兼容。具体方法如下:

  • FireWeapon():表示机器人发射武器。

  • Walk():表示机器人行走。

Robot 类的功能与 IPlayer 接口的功能相似,但方法名称和行为实现不同。


2.1.3 不兼容的部分

IPlayer 接口和 Robot 类的功能虽然相似,但接口定义不一致,导致它们无法直接协同工作。具体不兼容的部分如下:

功能IPlayer 接口Robot 类不兼容的原因
攻击行为Attack()FireWeapon()方法名称不同,IPlayer 期望调用 Attack(),而 Robot 提供的是 FireWeapon()
移动行为Move()Walk()方法名称不同,IPlayer 期望调用 Move(),而 Robot 提供的是 Walk()

2.1.4 不兼容的示例

假设客户端代码期望通过 IPlayer 接口控制角色行为:

IPlayer player = new Player(); // 假设Player是IPlayer的实现类
player.Attack(); // 期望执行攻击
player.Move();   // 期望执行移动

但如果我们直接使用 Robot 类:

Robot robot = new Robot();
robot.FireWeapon(); // 与Attack()不兼容
robot.Walk();       // 与Move()不兼容

由于方法名称和行为不一致,客户端代码无法直接使用 Robot 类。


2.1.5 适配器模式的作用

适配器模式的作用是将 Robot 类的接口适配到 IPlayer 接口,使得客户端代码可以通过 IPlayer 接口调用 Robot 类的方法。具体实现如下:

  • 类适配器模式:通过继承 Robot 类并实现 IPlayer 接口,将 Attack() 映射到 FireWeapon(),将 Move() 映射到 Walk()

  • 对象适配器模式:通过组合 Robot 类的实例并实现 IPlayer 接口,将 Attack() 委托给 FireWeapon(),将 Move() 委托给 Walk()


2.1.6 总结

  • IPlayer 接口:定义了客户端期望的接口(Attack() 和 Move())。

  • Robot 类:提供了实际的功能(FireWeapon() 和 Walk()),但接口与 IPlayer 不兼容。

  • 不兼容的部分:方法名称不同(Attack() vs FireWeapon()Move() vs Walk())。

  • 适配器模式的作用:通过适配器将 Robot 类的接口转换为 IPlayer 接口,解决接口不兼容问题。

3 . 类适配器模式的实现

任务目标
  1. 实现类适配器 RobotAdapter,确保适配后的 Attack() 方法调用 FireWeapon()Move() 方法调用 Walk()

  2. 理解继承的方式如何让适配器类直接复用 Robot 类的方法。

实现步骤
1. 创建类适配器 RobotAdapter

在 Adapters 文件夹中创建 RobotAdapter 类,继承 Robot 类并实现 IPlayer 接口。

    // RobotAdapter.cs - 类适配器类,继承Adaptee并实现Target接口public class RobotAdapter : Robot, IPlayer{// 将Target接口的Attack适配为Adaptee的FireWeaponpublic void Attack(){FireWeapon();  // 调用Robot的FireWeapon}// 将Target接口的Move适配为Adaptee的Walkpublic void Move(){Walk();  // 调用Robot的Walk}}
2. 代码解析
  • 继承 Robot 类RobotAdapter 继承了 Robot 类,因此可以直接使用 Robot 类的方法(如 FireWeapon() 和 Walk())。

  • 实现 IPlayer 接口RobotAdapter 实现了 IPlayer 接口,因此必须提供 Attack() 和 Move() 方法。

  • 方法适配

    • Attack() 方法内部调用 FireWeapon(),将 IPlayer 的 Attack() 适配为 Robot 的 FireWeapon()

    • Move() 方法内部调用 Walk(),将 IPlayer 的 Move() 适配为 Robot 的 Walk()

3. 测试类适配器

在 Program.cs 中编写测试代码,验证 RobotAdapter 的功能。

class Program
{static void Main(string[] args){// 使用类适配器将Robot适配为PlayerIPlayer player = new RobotAdapter();// 调用适配后的接口player.Attack();  // 实际调用的是Robot的FireWeaponplayer.Move();    // 实际调用的是Robot的Walk}
}

4. 运行结果

运行程序后,输出如下:

机器人发射武器!
机器人行走!

理解继承的方式如何让适配器类直接复用 Robot 类的方法
  1. 继承的作用

    • RobotAdapter 继承了 Robot 类,因此可以直接使用 Robot 类的所有公共方法(如 FireWeapon() 和 Walk())。

    • 继承使得 RobotAdapter 无需重新实现 Robot 类的功能,直接复用其方法。

  2. 复用方法的体现

    • 在 RobotAdapter 中,Attack() 方法直接调用 FireWeapon()Move() 方法直接调用 Walk()

    • 这些方法的具体实现来自 Robot 类,RobotAdapter 只是将其适配到 IPlayer 接口。

  3. 优点

    • 代码简洁:无需重新实现 Robot 类的功能。

    • 直接复用:通过继承,RobotAdapter 可以直接使用 Robot 类的方法。

  4. 缺点

    • 灵活性较低:类适配器只能适配一个 Adaptee 类(即 Robot 类),无法适配多个 Adaptee 类。

    • 继承关系可能导致类层次复杂。


总结
  • 实现类适配器:通过继承 Robot 类并实现 IPlayer 接口,将 Attack() 适配为 FireWeapon(),将 Move() 适配为 Walk()

  • 继承的作用:继承使得 RobotAdapter 可以直接复用 Robot 类的方法,无需重新实现。

  • 运行结果:程序输出 机器人发射武器! 和 机器人行走!,证明适配器模式功能实现。

  • 理解继承的复用:继承是类适配器模式的核心机制,通过继承直接复用 Adaptee 类的方法,但灵活性较低。

4. 对象适配器模式的实现

4.1 任务目标

  1. 编写对象适配器 RobotObjectAdapter,理解通过组合的方式如何适配接口。

  2. 理解对象适配器模式如何比类适配器模式更灵活。

4.2 实现步骤

1. 创建对象适配器 RobotObjectAdapter

在 Adapters 文件夹中创建 RobotObjectAdapter 类,通过组合 Robot 类的实例并实现 IPlayer 接口。

 // RobotObjectAdapter.cs - 对象适配器类,组合Adaptee对象public class RobotObjectAdapter : IPlayer{private Robot _robot;  // 持有Adaptee的实例// 构造函数中传入Adaptee对象public RobotObjectAdapter(Robot robot){_robot = robot;}// 实现Target接口,将Attack适配为FireWeaponpublic void Attack(){_robot.FireWeapon();  // 调用Adaptee的方法}// 实现Target接口,将Move适配为Walkpublic void Move(){_robot.Walk();  // 调用Adaptee的方法}}
2. 代码解析
  • 组合 Robot 实例RobotObjectAdapter 内部持有一个 Robot 类的实例(_robot),通过组合的方式实现适配。

  • 实现 IPlayer 接口RobotObjectAdapter 实现了 IPlayer 接口,因此必须提供 Attack() 和 Move() 方法。

  • 方法适配

    • Attack() 方法内部调用 _robot.FireWeapon(),将 IPlayer 的 Attack() 适配为 Robot 的 FireWeapon()

    • Move() 方法内部调用 _robot.Walk(),将 IPlayer 的 Move() 适配为 Robot 的 Walk()

4.3. 测试对象适配器

在 Program.cs 中编写测试代码,验证 RobotObjectAdapter 的功能。

class Program
{static void Main(string[] args){// 创建Robot实例Robot robot = new Robot();// 使用对象适配器将Robot适配为PlayerIPlayer player = new RobotObjectAdapter(robot);// 调用适配后的接口player.Attack();  // 实际调用的是Robot的FireWeaponplayer.Move();    // 实际调用的是Robot的Walk}
}

4.4  运行结果

运行程序后,输出如下:

机器人发射武器!
机器人行走!

4.5 理解对象适配器模式如何比类适配器模式更灵活

  1. 组合 vs 继承

    • 类适配器模式:通过继承 Adaptee 类实现适配,只能适配一个 Adaptee 类。

    • 对象适配器模式:通过组合 Adaptee 类的实例实现适配,可以适配多个 Adaptee 类。

  2. 灵活性体现

    • 适配多个 Adaptee 类:对象适配器模式可以在运行时动态传入不同的 Adaptee 实例,适配多个类。例如:

IPlayer player1 = new RobotObjectAdapter(new Robot());
IPlayer player2 = new RobotObjectAdapter(new AdvancedRobot()); // 适配另一个Adaptee类
    • 解耦:对象适配器模式将适配器与 Adaptee 类解耦,Adaptee 类的变化不会影响适配器的实现。

    • 符合设计原则:对象适配器模式遵循“组合优于继承”的原则,提高了代码的灵活性和可维护性。

  1. 类适配器模式的局限性

    • 类适配器模式通过继承实现,只能适配一个 Adaptee 类。

    • 继承关系可能导致类层次复杂,难以扩展。

  2. 对象适配器模式的优势

    • 更灵活:可以适配多个 Adaptee 类,动态切换适配对象。

    • 更易扩展:新增 Adaptee 类时,无需修改适配器代码。

    • 更符合面向对象设计原则:组合优于继承,降低了耦合度。


4.6 总结

  • 实现对象适配器:通过组合 Robot 类的实例并实现 IPlayer 接口,将 Attack() 适配为 FireWeapon(),将 Move() 适配为 Walk()

  • 组合的作用:组合使得 RobotObjectAdapter 可以动态适配不同的 Adaptee 类,提高了灵活性。

  • 运行结果:程序输出 机器人发射武器! 和 机器人行走!,证明适配器模式功能实现。

  • 对象适配器的灵活性:对象适配器模式比类适配器模式更灵活,支持适配多个 Adaptee 类,符合“组合优于继承”的设计原则。

5.  类适配器和对象适配器的应用场景与优缺点对比

对比项类适配器模式对象适配器模式
实现方式通过继承 Adaptee 类实现适配。通过组合 Adaptee 类的实例实现适配。
优点1. 代码简洁,直接复用 Adaptee 的方法。
2. 无需额外创建 Adaptee 实例。
1. 更灵活,可以适配多个 Adaptee 类。
2. 符合组合优于继承的原则。
缺点1. 只能适配一个 Adaptee 类。
2. 继承关系可能导致类层次复杂。
1. 需要额外创建 Adaptee 实例。
2. 代码量稍多。
适用场景1. Adaptee 类的方法可以直接复用。
2. 不需要适配多个 Adaptee 类。
1. 需要适配多个 Adaptee 类。
2. 需要更灵活的适配方式。

总结

  • 类适配器模式适合在 Adaptee 类的方法可以直接复用且不需要适配多个 Adaptee 类的场景,代码简洁但灵活性较低。

  • 对象适配器模式更适合需要适配多个 Adaptee 类或需要更灵活适配方式的场景,虽然代码量稍多,但扩展性和灵活性更高。


http://www.ppmy.cn/news/1560972.html

相关文章

25考研希望渺茫,工作 VS 二战,怎么选?

25考研希望不大,找工作VS二战,怎么选? 考研落幕,今年的考研可谓是难上加难,很多人甚至发出灵魂三问,直击25考研出题组 因为题难,感觉今年希望不大,也不想调剂,是就业好呢…

STM32-ADC模数转换

定义: ADC(Analog-Digital Converter)模拟-数字转换器 ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁 12位逐次逼近型ADC【表示转化的范围是0-2^12 - 1】,1us转换时间 输…

GNU链接器简介

GNU链接器简介 1 使用简单程序简介链接脚本1.1 测试程序1.2 编译测试程序1.2.1 不使用链接器编译1.2.1.1 不使用链接器编译1.2.1.2 读取objdump_test 的结构 1.2.2 使用链接器去链接1.2.2.1 链接脚本1.2.2.2 使用链接脚本编译1.2.2.3 读取objdump 的结构 2 链接脚本2.1 基本连接…

Spring Boot自动装配代码详解

概述 Spring Boot自动装配是其核心特性之一,它能够根据项目中添加的依赖自动配置Spring应用程序。通过自动装配,开发人员可以减少大量的配置工作,快速搭建起一个可用的Spring应用。 关键组件和注解 SpringBootApplication注解 这是Spring Bo…

【Spring Boot】Spring AOP 快速上手指南:开启面向切面编程新旅程

前言 🌟🌟本期讲解关于spring aop的入门介绍~~~ 🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 🔥 你的点赞就是小编不断更新的最大动力 🎆那么废话不…

2000-2020年各省地区生产总值数据/各省gdp数据

2000-2020年各省地区生产总值数据/各省gdp数据 1、时间:2000-2020年 2、来源:国家统计局 3、指标:行政区划代码、地区、年份、地区生产总值 4、范围:31省 指标解释:地区生产总值(Regional GDP&#xf…

GitHub 基础使用指南

GitHub 是一个流行的代码托管平台,主要用于版本控制和协作开发项目。它为开发者提供了强大的工具来管理项目、追踪问题、以及进行代码审查。 本文将介绍 GitHub 的一些基础功能,包括镜像站点、导航栏的使用、贡献设置、以及如何寻找特定代码或仓库。 1.…

欧几里得距离在权重矩阵中的物理意义

欧几里得距离在权重矩阵中的物理意义 目录 欧几里得距离在权重矩阵中的物理意义**衡量神经元差异程度**:**反映模型变化程度**:**聚类和分组的依据**:自然语言处理中的模型更新:**神经网络聚类分组**:欧几里得距离在权重矩阵中的物理意义衡量神经元差异程度: 在神经网络中…