导航
1应用场景
2.1类适配器
2.2对象适配器
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
<全文完>