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

1. 适配器模式简介

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

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

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

2. 定义接口

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

cs 复制代码
 // IPlayer.cs - Target接口
 public interface IPlayer
 {
     void Attack();  // 玩家角色攻击
     void Move();    // 玩家角色移动
 }
cs 复制代码
 // 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 接口控制角色行为:

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

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

cs 复制代码
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 接口。

cs 复制代码
    // 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. 代码解析
  • 继承 RobotRobotAdapter 继承了 Robot 类,因此可以直接使用 Robot 类的方法(如 FireWeapon()Walk())。

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

  • 方法适配

    • Attack() 方法内部调用 FireWeapon(),将 IPlayerAttack() 适配为 RobotFireWeapon()

    • Move() 方法内部调用 Walk(),将 IPlayerMove() 适配为 RobotWalk()

3. 测试类适配器

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

cs 复制代码
class Program
{
    static void Main(string[] args)
    {
        // 使用类适配器将Robot适配为Player
        IPlayer player = new RobotAdapter();
        
        // 调用适配后的接口
        player.Attack();  // 实际调用的是Robot的FireWeapon
        player.Move();    // 实际调用的是Robot的Walk
    }
}
4. 运行结果

运行程序后,输出如下:

cs 复制代码
机器人发射武器!
机器人行走!
理解继承的方式如何让适配器类直接复用 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 接口。

cs 复制代码
 // 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(),将 IPlayerAttack() 适配为 RobotFireWeapon()

    • Move() 方法内部调用 _robot.Walk(),将 IPlayerMove() 适配为 RobotWalk()

4.3. 测试对象适配器

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

cs 复制代码
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 运行结果

运行程序后,输出如下:

cs 复制代码
机器人发射武器!
机器人行走!

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

  1. 组合 vs 继承

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

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

  2. 灵活性体现

    • 适配多个 Adaptee :对象适配器模式可以在运行时动态传入不同的 Adaptee 实例,适配多个类。例如:
cs 复制代码
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 类或需要更灵活适配方式的场景,虽然代码量稍多,但扩展性和灵活性更高。

相关推荐
zeijiershuai15 分钟前
Java jdk8新特性:Stream 流
java·开发语言
YOULANSHENGMENG16 分钟前
linux上使用cmake编译的方法
开发语言·c++
学计算机的睿智大学生29 分钟前
关于python的数据分析与应用
开发语言·python·数据分析
晚安~~32 分钟前
共享充电宝系统|Java|SSM|VUE| 前后端分离
java·开发语言·tomcat·maven
找了一圈尾巴42 分钟前
Wend看源码-Java-Arrays 工具集学习
java·开发语言·学习
HelloZheQ43 分钟前
Java与AI:构建智能应用的强大组合
java·开发语言·人工智能
自律小仔44 分钟前
前端开发语言涉及到 的基本数据类型(Primitive Data Types)
开发语言·后端·golang
S-X-S1 小时前
八万字Java面试高频题目汇总(冲刺春招!)
java·开发语言·面试
Chenglin_Yu1 小时前
Python嵌套列表的浅拷贝与深拷贝
开发语言·python
码农丁丁1 小时前
[python3]Excel解析库-xlutils
开发语言·python·excel