适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
我们通过下面的实例来演示适配器模式的使用。其中,播放器设备只能播放 mp3 文件,通过使用一个更高级的播放器来播放 vlc 和 mp4 文件。

意图

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

主要解决

主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。

何时使用

  1. 系统需要使用现有的类,而此类的接口不符合系统的需要。
  2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
  3. 通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)

如何解决

继承或依赖(推荐)

关键代码

适配器继承或依赖已有的对象,实现想要的目标接口。

应用实例

  1. 美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。
  2. JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。
  3. 在 LINUX 上运行 WINDOWS 程序。
  4. JAVA 中的 jdbc。

优点

  1. 可以让任何两个没有关联的类一起运行。
  2. 提高了类的复用。
  3. 增加了类的透明度。
  4. 灵活性好。

缺点

  1. 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
  2. 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

使用场景

有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

注意事项

适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

示例

image.png

  1. #include "pch.h"
  2. #include <iostream>
  3. // 高级播放器接口
  4. class IAdvancedMediaPlayer
  5. {
  6. public:
  7. virtual void PlayMP3()
  8. {
  9. std::cout << "Not support" << std::endl;
  10. }
  11. virtual void PlayMP4()
  12. {
  13. std::cout << "Not support" << std::endl;
  14. }
  15. virtual void PlayVLC()
  16. {
  17. std::cout << "Not support" << std::endl;
  18. }
  19. };
  20. // MP4播放器
  21. class MP4Player : public IAdvancedMediaPlayer
  22. {
  23. public:
  24. virtual void PlayMP4()
  25. {
  26. std::cout << "Play a MP4" << std::endl;
  27. }
  28. };
  29. // VLC播放器
  30. class VLCPlayer : public IAdvancedMediaPlayer
  31. {
  32. public:
  33. virtual void PlayVLC()
  34. {
  35. std::cout << "Play a VLC" << std::endl;
  36. }
  37. };
  38. // 播放器接口
  39. class IMediaPlayer
  40. {
  41. public:
  42. virtual void Play(int mediaType) = 0;
  43. enum MediaType
  44. {
  45. T_MP3 = 0,
  46. T_MP4 = 1,
  47. T_VLC = 2
  48. };
  49. };
  50. // 高级播放器适配类
  51. class MediaAdapter : IMediaPlayer
  52. {
  53. public:
  54. MediaAdapter()
  55. {
  56. pMP4Player = new MP4Player();
  57. pVLCPlayer = new VLCPlayer();
  58. }
  59. ~MediaAdapter()
  60. {
  61. delete pMP4Player;
  62. pMP4Player = nullptr;
  63. delete pVLCPlayer;
  64. pVLCPlayer = nullptr;
  65. }
  66. virtual void Play(int mediaType)
  67. {
  68. switch (mediaType)
  69. {
  70. case MediaType::T_MP4:
  71. {
  72. pMP4Player->PlayMP4();
  73. break;
  74. }
  75. case MediaType::T_VLC:
  76. {
  77. pVLCPlayer->PlayVLC();
  78. break;
  79. }
  80. default:
  81. {
  82. std::cout << "Not support" << std::endl;
  83. break;
  84. }
  85. }
  86. }
  87. private:
  88. IAdvancedMediaPlayer* pMP4Player;
  89. IAdvancedMediaPlayer* pVLCPlayer;
  90. };
  91. // 普通播放器
  92. class MediaPlayer : public IMediaPlayer
  93. {
  94. public:
  95. MediaPlayer()
  96. {
  97. mediaAdapter = new MediaAdapter();
  98. }
  99. ~MediaPlayer()
  100. {
  101. delete mediaAdapter;
  102. mediaAdapter = nullptr;
  103. }
  104. virtual void Play(int mediaType)
  105. {
  106. switch (mediaType)
  107. {
  108. // 使用自带MP3播放功能
  109. case MediaType::T_MP3:
  110. {
  111. std::cout << "Play a MP3" << std::endl;
  112. break;
  113. }
  114. // 使用适配器提供的MP4/VLC播放功能
  115. case MediaType::T_MP4:
  116. case MediaType::T_VLC:
  117. {
  118. mediaAdapter->Play(mediaType);
  119. break;
  120. }
  121. default:
  122. {
  123. std::cout << "Not support" << std::endl;
  124. break;
  125. }
  126. }
  127. }
  128. private:
  129. MediaAdapter* mediaAdapter;
  130. };
  131. // 调用示例
  132. int main()
  133. {
  134. MediaPlayer mediaPlayer;
  135. mediaPlayer.Play(MediaPlayer::MediaType::T_MP3);
  136. mediaPlayer.Play(MediaPlayer::MediaType::T_MP4);
  137. mediaPlayer.Play(MediaPlayer::MediaType::T_VLC);
  138. }