当前位置: 首页 > article >正文

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

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的FireWeapon
        public void Attack()
        {
            FireWeapon();  // 调用Robot的FireWeapon
        }
        // 将Target接口的Move适配为Adaptee的Walk
        public 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适配为Player
        IPlayer player = new RobotAdapter();
        
        // 调用适配后的接口
        player.Attack();  // 实际调用的是Robot的FireWeapon
        player.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适配为FireWeapon
     public void Attack()
     {
         _robot.FireWeapon();  // 调用Adaptee的方法
     }
     // 实现Target接口,将Move适配为Walk
     public 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适配为Player
        IPlayer player = new RobotObjectAdapter(robot);
        
        // 调用适配后的接口
        player.Attack();  // 实际调用的是Robot的FireWeapon
        player.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.kler.cn/a/465687.html

相关文章:

  • 云打印之菜鸟打印组件交互协议
  • vue elementUI Plus实现拖拽流程图,不引入插件,纯手写实现。
  • 下载b站高清视频
  • Fabric部署-docker安装
  • 图像分割基础:使用Python和scikit-image库
  • SpringBoot中实现拦截器和过滤器
  • 高频java面试题
  • 用语言模型 GLM-Zero-Preview 来驱动战场推演
  • 数据挖掘——支持向量机分类器
  • Centos源码安装MariaDB 基于GTID主从部署(一遍过)
  • Redis面试相关
  • vue2框架配置路由设计打印单
  • 【Axios使用手册】如何使用axios向后端发送请求并进行数据交互
  • 利用PHP爬虫获取1688按关键字搜索商品:技术解析与实践指南
  • 【C语言程序设计——循环程序设计】枚举法换硬币(头歌实践教学平台习题)【合集】
  • 【HTTP和gRPC的区别】协议类型/传输效率/性能/语义/跨语言支持/安全性/使用场景/易用性对比
  • Kafka详解 ③ | Kafka集群操作与API操作
  • 常用的聚合函数
  • TCPDump参数详解及示例
  • 组合模式——C++实现
  • UniApp | 从入门到精通:开启全平台开发的大门
  • SpringSpringBoot常用注解总结
  • oceanbase集群访问异常问题处理
  • 高温大面积碳化硅外延生长装置及处理方法
  • 问题清除指南|关于num_classes与 BCELoss、BCEWithLogitsLoss 和 CrossEntropyLoss 的关系
  • 【论文阅读笔记】SCI算法与代码 | 低照度图像增强 | 2022.4.21