开发当中我们经常遇到这样的情况:有一些功能自己开发需要花费巨大的成本,但是现存的可供使用的第三方库还不太成熟,我们可以暂时使用这些库。但是,一旦需要修改依赖库的时候,源码也需要大规模地修改,有没有什么方法能够最大限度地降低修改的幅度呢?这时候我们可以考虑使用adapter模式。
一、定义
适配器模式是一种结构型模式。它的目的是使一个类的接口转换成客户希望的另一种接口,适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。
二、结构
适配器模式有两种类型
1.对象适配器模式
在这种模式中,适配器类包含适配对象的实例,这样适配器类就可以调用适配对象的方法了。
2.类适配模式
这种类型的适配器通过多重继承或者实现多个现存的和期望实现(兼容)的类/接口。对于java这种不支持多重继承的语言,一般将待适配的对象声明为Interface。
参数说明:
Target : Client使用的现存的类/接口
Adaptee : 待适配的类/接口
Adapter : 实现Target与Adaptee适配的类
三、适用性
- 想使用一个已经存在的类,但是它的接口不符合你的需求
四、举例
最后让我们来看一个例子,假设我们有个MediaPlayer的Interface,包含play()抽象方法,AudioPlayer实现了MediaPlayer方法,默认play()的是mp3的格式。还有一个AdvancedMediaPlayer的接口,包含playAvi()和playMp4()两个抽象方法,AviPlayer和Mp4Player实现了AdvancedMediaPlayer。现在如果想让AudioPlayer也能支持mp4和avi两种格式的播放,应该怎么办?这里我们可以使用对象适配模型,创建一个MediaAdapter的类,在其中包含一个AdvancedMediaPlayer的类实例,这样就可以调用它的子类方法了。然后给AudioPlayer增添一个MediaPlayer的实例,判断一下输入音乐格式,做出不同的操作就行了。
下面贴代码:
MediaPlayer:
package designpattern.adapter; public interface MediaPlayer { public void play(String audioType, String fileName); }
AdvancedMediaPlayer:
package designpattern.adapter; public interface AdvancedMediaPlayer { public void playAvi(String fileName); public void playMp4(String fileName); }
AviPlayer:
package designpattern.adapter; public class AviPlayer implements AdvancedMediaPlayer { @Override public void playAvi(String fileName) { // TODO Auto-generated method stub System.out.println("Playing avi , file name is "+fileName); } @Override public void playMp4(String fileName) { // TODO Auto-generated method stub } }
Mp4Player:
package designpattern.adapter; public class Mp4Player implements AdvancedMediaPlayer { @Override public void playAvi(String fileName) { // TODO Auto-generated method stub } @Override public void playMp4(String fileName) { // TODO Auto-generated method stub System.out.println("Playing mp4, file name is"+fileName); } }
MediaAdapter:
package designpattern.adapter; public class MediaAdapter implements MediaPlayer { private AdvancedMediaPlayer advancedMediaPlayer; public MediaAdapter(String audioType){ if(audioType.equalsIgnoreCase("mp4")){ advancedMediaPlayer = new Mp4Player(); }else if(audioType.equalsIgnoreCase("avi")){ advancedMediaPlayer = new AviPlayer(); }else{ throw new IllegalArgumentException("unrecognized AdvancedMediaPlayer type"); } } @Override public void play(String audioType, String fileName) { // TODO Auto-generated method stub if(audioType.equalsIgnoreCase("avi")){ advancedMediaPlayer.playAvi(fileName); }else if(audioType.equalsIgnoreCase("mp4")){ advancedMediaPlayer.playMp4(fileName); }else{ throw new IllegalArgumentException("unrecognized audio type for advancedMediaPlayer"); } } }
AudioAdapter:
package designpattern.adapter; public class AudioPlayer implements MediaPlayer { private MediaAdapter mediaAdapter; @Override public void play(String audioType, String fileName) { if (audioType.equalsIgnoreCase("mp3")) { System.out.println("Playing mp3, file name is " + fileName); } else if (audioType.equalsIgnoreCase("mp4") || audioType.equalsIgnoreCase("avi")) { mediaAdapter = new MediaAdapter(audioType); mediaAdapter.play(audioType, fileName); }else{ System.out.println("unrecognized audio format"); } } }
测试类,MediaDemo:
package designpattern.adapter; public class MediaDemo { public static void main(String[] args) { MediaPlayer player = new AudioPlayer(); player.play("mp3","1.mp3"); player.play("mp4","2.mp4"); player.play("avi","3.avi"); player.play("mpg","4.mpg"); } }
控制台输出:
Playing mp3, file name is 1.mp3 Playing mp4, file name is2.mp4 Playing avi , file name is 3.avi unrecognized audio format
参考文献:
2.design_pattern adapter_pattern