满纸荒唐言,一把心酸泪,都云作者痴,谁解其中味。 技术博客 心情随笔 登录
设计模式:适配器
2025/6/4 29

导航

1应用场景

2如何在适配器中包装源对象

2.1类适配器

2.2对象适配器

3如何解决目标接口臃肿的问题

4总结

1 应用场景

适配器模式

在现实生活中,经常会出现两个事物无法协同工作而需要第三者协调适配的情况。比如用电脑访问相机的SD卡需要一个读卡器;用直流电的笔记本使用交流电源时需要一个电源适配器;讲中文的人与讲英文的人对话时需要一个翻译等等。

适配器模式还有一个形象的别名叫包装器模式(Wrapper),通过包装来源,来实现目标接口。来源:https://www.wubayue.com

2 如何在适配器中包装源对象

2.1 类适配器

类适配器通过继承来源类,来适配目标接口:

类适配器

来源类:

// 来源类:中国人用中文交流
public class Chinese
{
    // 听中文说中文
    public string SpeakChinese(string listen)
    {
        if (listen == "您好")
            return "您好";
        return string.Empty;
    }
} 

目标接口:

// 目标接口:需要英文交流
public interface IEnglish
{
    // 听英文说英文
    string SpeakEnglish(string listen);
} 

类适配器:

// 类适配器:通过继承来源类来实现目标接口
public class Translator : Chinese, IEnglish
{
    // 实现目标接口
    public string SpeakEnglish(string listen)
    {
        // 英译汉
        string listenChinese = string.Empty;
        if (listen == "Hello")
            listenChinese = "您好";

        // 调用父类
        string speakChinese = string.Empty;
        if (listenChinese.Length > 0)
            speakChinese = base.SpeakChinese(listenChinese);

        // 汉译英
        if (speakChinese == "您好")
            return "Hello";
        return string.Empty;
    }
} 

调用示例:

static void Main(string[] args)
{
    // 通过翻译(类适配器)达成了中英文交流
    IEnglish english = new Translator();
    // 输出:Hello
    Console.WriteLine(english.SpeakEnglish("Hello"));
} 

2.2 对象适配器

对象适配器通过注入来源对象,来适配目标接口。对象适配器比类适配器更灵活,扩展性也更强,它除了支持注入多个来源对象,还通过抽象支持同一个来源对象的不同实现:

对象适配器

来源类:

// 源对象:中国人用中文交流
public interface IChinese
{
    // 听中文说中文
    string SpeakChinese(string listen);
}

public class Chinese : IChinese
{
    public string SpeakChinese(string listen)
    {
        if (listen == "您好")
            return "您好";
        return string.Empty;
    }
} 

目标接口:

// 目标接口:需要英文交流
public interface IEnglish
{
    // 听英文说英文
    string SpeakEnglish(string listen);
} 

对象适配器:

// 对象适配器:通过注入源对象来实现目标接口
public class Translator : IEnglish
{
    // 包装源对象
    private IChinese _chinese;
    public Translator(IChinese chinese)
    {
        _chinese = chinese;
    }

    // 实现目标接口
    public string SpeakEnglish(string listen)
    {
        // 英译汉
        string listenChinese = string.Empty;
        if (listen == "Hello")
            listenChinese = "您好";

        // 调用源对象
        string speakChinese = string.Empty;
        if (listenChinese.Length > 0)
            speakChinese = _chinese.SpeakChinese(listenChinese);

        // 汉译英
        if (speakChinese == "您好")
            return "Hello";
        return string.Empty;
    }
} 

调用示例:来源:https://www.wubayue.com

static void Main(string[] args)
{
    // 构造源对象
    Adapter.ObjectAdapter.IChinese chinese = new Adapter.ObjectAdapter.Chinese();
    // 通过翻译(适配器)达成了中英文交流
    Adapter.ObjectAdapter.IEnglish english = new Adapter.ObjectAdapter.Translator(chinese);
    // 输出:Hello
    Console.WriteLine(english.SpeakEnglish("Hello"));
} 

3 如何解决目标接口臃肿的问题

适配器衔接了来源与目标两个方向,上一章描述了针对来源的不同包装方式,本章将主要探讨如何解决目标接口的臃肿问题。接口臃肿其实并不仅仅存在于适配器设计模式中,在其它业务场景以及日常生活中也会遇到,比如你去办一张新的银行卡,银行让你先填一个好几页的表格,当你一筹莫展的时候,工作人员会告诉你在关键位置填上关键信息即可,其它的空着就行。

软件设计中的解决方案其实与银行类似,提供一个模板,其中每个项都有默认值(默认适配器),当某些项被填写的时候,就替换掉对应的默认值(子类重写)。

默认适配器

目标接口:

// 臃肿的目标接口
public interface IEnglish
{
    // 宗教信仰
    string Religion();
    // 是否拥有枪支
    bool Guns();
    // 听英文说英文
    string SpeakEnglish(string listen);
} 

默认适配器:

// 默认适配器:实现目标接口
public class Translator : IEnglish
{
    // 默认适配器提供了针对目标接口的默认实现,并允许在其子类中按需重写
    public virtual string Religion() { return string.Empty; }
    public virtual bool Guns() { return false; }
    public virtual string SpeakEnglish(string listen) { return string.Empty; }
} 

具体适配器:

// 来源类:中国人用中文交流
public interface IChinese
{
    string SpeakChinese(string listen);
}
public class Chinese : IChinese
{
    public string SpeakChinese(string listen)
    {
        if (listen == "您好")
            return "您好";
        return string.Empty;
    }
}

// 具体适配器:中文翻译
public class ChineseTranslator : Translator
{
    // 包装源对象
    private IChinese _chinese;
    public ChineseTranslator(IChinese chinese)
    {
        _chinese = chinese;
    }

    // 实现目标接口,聚焦目标过滤臃肿
    public override string SpeakEnglish(string listen)
    {
        // 英译汉
        string listenChinese = string.Empty;
        if (listen == "Hello")
            listenChinese = "您好";

        // 调用源对象
        string speakChinese = string.Empty;
        if (listenChinese.Length > 0)
            speakChinese = _chinese.SpeakChinese(listenChinese);

        // 汉译英
        if (speakChinese == "您好")
            return "Hello";
        return string.Empty;
    }
} 
// 来源类:日本人用日语交流
public interface IJapanese
{
    string SpeakJapanese(string listen);
}
public class Japanese : IJapanese
{
    public string SpeakJapanese(string listen)
    {
        if (listen == "こんにちは")
            return "こんにちは";
        return string.Empty;
    }
}

// 具体适配器:日语翻译
public class JapaneseTranslator : Translator
{
    // 包装源对象
    private IJapanese _japanese;
    public JapaneseTranslator(IJapanese japanese)
    {
        _japanese = japanese;
    }

    // 实现目标接口,聚焦目标过滤臃肿
    public override string SpeakEnglish(string listen)
    {
        // 英译日
        string listenJapanese = string.Empty;
        if (listen == "Hello")
            listenJapanese = "こんにちは";

        // 调用源对象
        string speakJapanese = string.Empty;
        if (listenJapanese.Length > 0)
            speakJapanese = _japanese.SpeakJapanese(listenJapanese);

        // 日译英
        if (speakJapanese == "こんにちは")
            return "Hello";
        return string.Empty;
    }
} 

调用示例:来源:https://www.wubayue.com

static void Main(string[] args)
{
    // 中文翻译
    Adapter.AdapterDefault.IChinese chinese = new Adapter.AdapterDefault.Chinese();
    Adapter.AdapterDefault.Translator chineseTranslator = new Adapter.AdapterDefault.ChineseTranslator(chinese);
    Console.WriteLine("Chinese translator : " + chineseTranslator.SpeakEnglish("Hello"));
    // 日语翻译
    Adapter.AdapterDefault.IJapanese japanese = new Adapter.AdapterDefault.Japanese();
    Adapter.AdapterDefault.Translator japaneseTranslator = new Adapter.AdapterDefault.JapaneseTranslator(japanese);
    Console.WriteLine("Japanese translator : " + japaneseTranslator.SpeakEnglish("Hello"));
} 

4 总结

适配器模式是一种常用的结构型设计模式,其主要目的是解决两个软件模块之间的兼容问题,在不修改原有代码的基础上,使来源与目标代码能够兼容并协同工作。本文以类图结合代码示例的方式,详细描述了如何在适配器中包装源对象,以及如何解决目标接口臃肿的问题。常在河边走,哪有不湿鞋,掌握适配器模式,将为你解决新旧代码的兼容提供通用范式。来源:https://www.wubayue.com

<全文完>