适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。可以将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。这里的读卡器就相当于一个适配器。
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用:?
- 系统需要使用现有的类,而此类的接口不符合系统的需要。
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
- 通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
如何解决:继承或依赖(推荐)。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
优点:?
- 可以让任何两个没有关联的类一起运行。
- 提高了类的复用。
- 增加了类的透明度。
- 灵活性好。
缺点:?
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
- 由于部分高级语言(C#、Java) 至多继承一个类,所以至多只能适配一个适配者类。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
实例应用1:
下面的实例来演示适配器模式的使用。其中,音频播放器设备只能播放 mp3 文件,通过使用一个更高级的音频播放器来播放 vlc 和 mp4 文件。
默认情况下,AudioPlayer只能播放mp3文件,VlcPlayer播放vlc文件,Mp4Player播放mp4文件。如下所示:
?现在我们希望AudioPlayer也能播放vlc文件和mp4文件。也就是说希望能有一个实现了IMediaPlayer,针对IAdvancedMediaPlayer做适配的适配器类MediaPlayerAdapterAdvancedMediaPlayer。
同时修改IMediaPlayer接口,让它能根据传入的不同音频类型播放不同类型的文件。
IAdvancedMediaPlayer
namespace Adapter
{
public interface IAdvanceMediaPlayer
{
void PlayVlc(string fileName);
void PlayMp4(string fileName);
}
}
IMediaPlayer
namespace Adapter
{
public interface IMediaPlayer
{
void Play(EAudioType audioType, string fileName);
}
}
VlcPlayer、Mp4Player
using UnityEngine;
namespace Adapter
{
public class VlcPlayer : IAdvanceMediaPlayer
{
public void PlayVlc(string fileName)
{
Debug.Log($"Playing vlc file. Name {fileName}");
}
public void PlayMp4(string fileName)
{
}
}
public class Mp4Player : IAdvanceMediaPlayer
{
public void PlayVlc(string fileName)
{
}
public void PlayMp4(string fileName)
{
Debug.Log($"Playing mp4 file. Name {fileName}");
}
}
}
适配器类MediaPlayerAdapterAdvancedMediaPlayer?
namespace Adapter
{
public class MediaPlayerAdapterAdvancedMediaPlayer : IMediaPlayer
{
private IAdvanceMediaPlayer _advanceMediaPlayer;
public MediaPlayerAdapterAdvancedMediaPlayer(EAudioType audioType)
{
if (audioType == EAudioType.Vlc)
{
_advanceMediaPlayer = new VlcPlayer();
}
else if (audioType == EAudioType.Mp4)
{
_advanceMediaPlayer = new Mp4Player();
}
}
public void Play(EAudioType audioType, string fileName)
{
if (audioType == EAudioType.Vlc)
{
_advanceMediaPlayer.PlayVlc(fileName);
}
else if (audioType == EAudioType.Mp4)
{
_advanceMediaPlayer.PlayMp4(fileName);
}
}
}
}
AudioPlayer
using UnityEngine;
namespace Adapter
{
public class AudioPlayer : IMediaPlayer
{
private MediaPlayerAdapterAdvancedMediaPlayer _mediaAdapter;
public void Play(EAudioType audioType, string fileName)
{
if (audioType == EAudioType.Mp3)
{
Debug.Log($"Playing map3 file. Name {fileName}");
}
else if (audioType == EAudioType.Vlc ||
audioType == EAudioType.Mp4)
{
_mediaAdapter = new MediaPlayerAdapterAdvancedMediaPlayer(audioType);
_mediaAdapter.Play(audioType, fileName);
}
else
{
Debug.LogError($"Invalid Media. {audioType} Format Not Supported.");
}
}
}
}
AudioPlayer现在可以播放不同类型的音频文件了。
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.Play(EAudioType.Mp3,"mp3");
audioPlayer.Play(EAudioType.Mp4,"mp4");
audioPlayer.Play(EAudioType.Vlc,"vlc");
实例应用2:
模拟计算机读取SD卡:
创建SD卡的接口及实现类:
public interface ISDCard
{
string ReadSD();
int WriteSD(string msg);
}
public class SDCard: ISDCard
{
public string ReadSD()
{
string msg = "sdcard read a msg :hello word SD";
return msg;
}
public int WriteSD(string msg)
{
Debug.Log($"sd card write msg : {msg}");
return 1;
}
}
创建计算机接口及实现类提供读取SD卡方法:
public interface IComputer
{
string ReadSD(ISDCard sdCard);
}
public class ThinkpadComputer : IComputer
{
public string ReadSD(ISDCard sdCard)
{
if (sdCard == null)
{
throw new NullReferenceException("sd card null");
}
return sdCard.ReadSD();
}
}
现在去使用计算机实例读取一张SD卡:
IComputer computer = new ThinkpadComputer();
ISDCard sdCard = new SDCard();
computer.ReadSD(sdCard);
我们希望在不改变计算机读取SD卡接口的情况下,读取TF卡,已知TF卡接口及实现类如下:
public interface ITFCard
{
string ReadTF();
int WriteTF(string msg);
}
public class TFCard : ITFCard
{
public string ReadTF()
{
string msg ="tf card reade msg : hello word tf card";
return msg;
}
public int WriteTF(string msg)
{
Debug.Log("tf card write a msg : {msg}");
return 1;
}
}
现在我们需要一个适配器,它实现了ISDCard接口,但传入的是ITFCard,即针对ISDCard兼容ITFCard。
public class SDAdapterTF : ISDCard
{
private ITFCard tfCard;
public SDAdapterTF(ITFCard tfCard)
{
this.tfCard = tfCard;
}
public String ReadSD()
{
Debug.Log("adapter read tf card ");
return tfCard.ReadTF();
}
public int WriteSD(String msg)
{
Debug.Log("adapter write tf card");
return tfCard.WriteTF(msg);
}
}
那么现在就可以以ISDCard的实例传入,但内部实现的却是ITFCard的实现方法。
ITFCard tfCard = new TFCard();
ISDCard sdCardAdapterTf = new SDAdapterTF(tfCard);
computer.ReadSD(sdCardAdapterTf);
类图设计如下:
|