Menu
CoddyTech

适配器模式

本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 57 课,共 70 课。

适配器模式是一种结构型模式,能够让不兼容的接口协同工作。它充当两个类之间的桥梁,将一个类的接口转换为客户端所期望的接口。你可以把它想象成一个电源适配器,让你能把欧洲的设备插入美国的插座。

当你想使用现有的类,但它的接口与你的代码需求不匹配时,这种模式特别有用。你可以创建一个适配器来包装它,而不是修改原来的类(你可能无法控制它):

// 现有类的接口不兼容
public class OldPrinter
{
    public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}

// 你的代码所需的接口
public interface IPrinter
{
    void Print(string text);
}

// 适配器连接两个接口
public class PrinterAdapter : IPrinter
{
    private OldPrinter _oldPrinter;
    
    public PrinterAdapter(OldPrinter oldPrinter)
    {
        _oldPrinter = oldPrinter;
    }
    
    public void Print(string text)
    {
        _oldPrinter.PrintMessage(text);  // 委托给旧接口处理
    }
}

现在,你的代码可以通过预期的接口与旧打印机配合工作:

OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);

printer.Print("Hello");  // 旧版:Hello

在将第三方库或旧版代码集成到应用程序时,适配器模式非常有用。客户端代码通过 IPrinter 接口进行操作,完全不知道幕后实际执行工作的类是一个旧版且不兼容的类。

challenge icon

挑战

简单

让我们使用适配器模式构建一个媒体播放器系统。你有一个现成的音频库,可以播放 MP3 文件,但你的应用需要一个能够处理不同媒体类型的统一接口。你将创建一个适配器来弥合这一差距,让现代媒体播放器能够与旧版音频库无缝协作。

你将把代码分成三个文件:

  • LegacyAudio.cs:这是一个你无法修改的现有库。在 MediaSystem 命名空间中创建一个 LegacyAudioPlayer 类,其中包含一个 PlayMp3(string filename) 方法,该方法返回字符串 "Playing MP3: {filename}"。这是你的“被适配者”,也就是接口不兼容的类。
  • MediaAdapter.cs:在同一个命名空间中创建接口和适配器。定义一个 IMediaPlayer 接口,其中包含一个返回字符串的 Play(string filename) 方法。然后创建一个实现 IMediaPlayer 的 MediaPlayerAdapter 类。你的适配器应包装一个 LegacyAudioPlayer 实例(通过构造函数传入),并将 Play 调用转换为旧版播放器的 PlayMp3 方法调用。
  • Program.cs:创建旧版播放器,用你的适配器包装它,并通过 IMediaPlayer 接口使用适配器,将所有内容整合起来。这展示了客户端代码如何使用现代接口,同时由适配器在幕后处理与旧版系统的通信。

你将收到一个输入:

  • 要播放的文件名(例如,song.mp3)

创建一个 LegacyAudioPlayer,用 MediaPlayerAdapter 包装它,然后通过 IMediaPlayer 接口在适配器上调用 Play,并打印结果。

例如,如果输入是 favorite_track.mp3,输出应为:

Playing MP3: favorite_track.mp3

这种模式的优点在于,你的主代码只与 IMediaPlayer 交互,它完全不知道实际工作是由旧版音频库完成的!如果你之后需要支持另一个旧版库,只需创建一个新的适配器,无需更改任何客户端代码!

亲手试试

using System;
using MediaSystem;

class Program
{
    public static void Main(string[] args)
    {
        // 读取输入的文件名
        string filename = Console.ReadLine();
        
        // TODO: 创建一个 LegacyAudioPlayer 实例
        
        // TODO: 用 MediaPlayerAdapter 包装它
        
        // TODO: 通过 IMediaPlayer 接口使用适配器
        // 并打印以文件名为参数调用 Play 的结果
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C# 在线编译器