- 定义:将一个类的接口转换成客户期望的另一个接口,使原本接口不兼容的类可以一起工作
- 类型:结构型
- 适用场景:
- 已经存在的类,它的方法和需求不匹配时(方法结果相同或相似)
- 不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案
- 优点:
- 能提高类的透明性和复用,现有的类复用但不需要改变,解决了现有类和目标类不匹配的问题
- 目标类和适配器类解耦,提高程序扩展性
- 符合开闭原则,具体的实现都在适配器中,客户端知道的只有适配器类,扩展也只需要扩展适配器类即可
- 缺点:
- 适配器编写过程需要全面考虑,可能会增加系统的复杂性
- 增加系统代码可读的难度
- 扩展:
- 对象适配器:符合组合复用原则,使用委托机制
- 类适配器:通过类继承来实现
- 相关设计模式:
- 适配器模式和外观模式:都是对现有类,现有系统的封装,外观定义了新的接口,而适配器则是复用原有的接口,适配器是两个已有的接口协同工作,而外观则是在现有的系统中提供一个更为方便的访问入口,外观模式是适配整个子系统,所以外观针对的对象力度更大
- 适配器模式和装饰者模式:装饰者模式重点在于增强,适配器模式重点在于适配
播放器接口及实现coding
/** * <p>播放器接口 </p> */ public interface IPlayer { /** * 播放MV */ void playMVs(); /** * 播放音乐 == 与Mp3播放音乐的功能【方法】名称一致 */ void playMusics(); /** * 播放电影 */ void playMovies(); } /** * <p>MP3播放器 == 功能单一,只播放歌曲</p> */ public class Mp3 { private String name = ""; public Mp3() { } public Mp3(String name) { this.name = name; } /** * 播放歌曲 == 凡是能播放音乐的设备,都要适配Mp3类的这个方法 * 如果你说鼠标能播放歌曲,Ok,也不是做不到,哈哈 */ public void playMusics() { System.out.println("Mp3 -- " + this.name + "播放歌曲"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
类适配器coding
/** * <p> * 适配器 == QQ播放器,将Mp3播放音乐的功能直接"窃取"到QQ播放器里 * 即QQ播放器无需再实现播放接口中的播放音乐的功能了 * </p> */ public class QQPlayer extends Mp3 implements IPlayer { @Override public void playMVs() { System.out.println("QQ播放器实现播放MV功能"); super.playMVs(); } @Override public void playMovies() { System.out.println("QQ播放器实现播放电影功能"); super.playMovies(); } }
UML
对象适配器coding
/** * <p>适配器 == 暴风音影播放器,将Mp3播放音乐的功能直接扩展到播放器里,即无需再实现播放器播放音乐的功能了</p> */ public class BaoFengPlayer implements IPlayer { /** * 唯一不同于QQ适配器的地方就是,暴风适配器里面可以创建Mp3的实例 * 即可以实现不同原对象播放音乐的方法,无需再继承Mp3类 */ private Mp3 mp3; public BaoFengPlayer(Mp3 mp3) { this.mp3 = mp3; } @Override public void playMVs() { System.out.println("暴风影音实现播放MV功能"); } @Override public void playMusics() { this.mp3.playMusics(); } @Override public void playMovies() { System.out.println("暴风影音实现播放电影功能"); } public Mp3 getMp3() { return mp3; } public void setMp3(Mp3 mp3) { this.mp3 = mp3; } }
UML
接口适配器
/** * <p> * 1、播放器抽象类 == 实现播放接口 * 2、抽象类中的非抽象方法:子类按需实现 * 3、抽象类中的抽象方法:子类必须全部实现 * 4、接口中的方法:实现类必须全部实现 * </p> */ public abstract class AbstractPlayer implements IPlayer { /** * 播放MV */ @Override public void playMVs() { } /** * 播放音乐 */ @Override public void playMusics() { } /** * 播放电影 */ @Override public void playMovies() { } /** * 播放器功能展示抽象方法,子类中必须实现 */ public abstract void show(); } /** * <p>播放器A == 只具有播放歌曲的功能</p> */ public class APlayer extends AbstractPlayer{ /** * 重写父类方法 */ @Override public void playMusics() { System.out.println("A实现播放音乐的功能"); } @Override public void show(){ System.out.println("=====播放器A功能展示:"); playMusics(); playMVs(); playMovies(); } } /** * <p>播放器B == 只具有播放MV的功能</p> */ public class BPlayer extends AbstractPlayer{ /** * 重写父类方法 */ @Override public void playMVs() { System.out.println("B实现播放MV的功能"); } @Override public void show(){ System.out.println("=====播放器B功能展示:"); playMusics(); playMVs(); playMovies(); } /** * <p>播放器C == 即能播放音乐、MV、又能播放电影</p> */ public class CPlayer extends AbstractPlayer{ /** * 重写父类播放MV方法 */ @Override public void playMVs() { System.out.println("C实现播放MV的功能"); } @Override public void playMusics() { System.out.println("C实现播放音乐的功能"); } @Override public void playMovies() { System.out.println("C实现播放电影的功能"); } @Override public void show(){ System.out.println("=====播放器C功能展示:"); playMusics(); playMVs(); playMovies(); } }
UML
测试
/** * <p>适配器模式测试 </p> */ public class AdapterTest { public static void main(String[] args){ // 使用类适配器 useAdapterOfClass(); // 使用对象适配器 useAdapterOfObject(); // 使用接口适配器 useAdapterOfInterFace(); /** * 类适配器:当一个类A想拥有某个接口I里的所有功能时,却又懒得实现接口中的M方法时 * 则可以专门定义一个类B,B中的方法和接口I中的M方法保持一致,只不过B中的M方法已经通过定制进行了功能实现 * 这时候,我们称B是一个待适配的类,而B中的M方法我们称原方法,接口I中的M方法称作目标方法 * A类我们则称呼为真正要适配的类,这个类通过继承B类、实现接口I即可具有目标接口I中的所有功能 * 正如一开始说的,适配的类A,懒得实现接口I中的M方法,怎么办,那我们就专门给A开个小灶,交给B类来做就好了 * * 对象适配器:其实和类适配器差不多,唯一的区别就是,类适配器无法对B类进行实例化,不够灵活,而对象适配器中 * 则将B类的实例放在了真正适配的类中,不管是内部还是外部,都可以实例化B对象,实现不同B对象的原始方法 * * 接口适配器:类适配和对象适配器有个不好的地方就是,适配器对象必须实现接口I中的所有功能 * 但现实是,我有时候不太想要I中的某些功能,怎么办? * 比如A对象只实现I中的M方法,B对象只实现I中的N方法,而C对象则全部实现I中的M、N、P方法 * 这时候就需要借助抽象类Ab了,Ab实现接口I,抽象类和接口的区别就是,抽象类中的方法无需都实现, * 而接口中的方法必须全部实现,这样一来,我们适配的类就可以通过只继承Ab类来实现接口I中的部分功能了 * * 项目中,具体用到哪种模式的适配器,可以好好斟酌一下了,其实我在项目中用到最多的是接口适配器模式 * */ } private static void useAdapterOfClass() { // 类适配器模式测试 == 持有类Mp3的原始方法,但是不能创建该类的实例 IPlayer qqPlayer = new QQPlayer(); qqPlayer.playMusics(); qqPlayer.playMVs(); qqPlayer.playMovies(); System.out.println("============分割线"); } private static void useAdapterOfObject() { // 对象适配器模式测试 == 持有类Mp3的原始方法,但是可以创建不同的Mp3类的实例 Mp3 sonyMp3 = new Mp3("索尼(SONY) NW-ZX300A"); IPlayer bfPlayer = new BaoFengPlayer(sonyMp3); bfPlayer.playMusics(); bfPlayer.playMVs(); bfPlayer.playMovies(); System.out.println("=========暴风影音升级"); // 暴风音影升级,改用纽曼的Mp3播放音乐功能 Mp3 newsMyMp3 = new Mp3("纽曼(Newsmy)F35"); ((BaoFengPlayer)bfPlayer).setMp3(newsMyMp3); bfPlayer.playMusics(); bfPlayer.playMVs(); bfPlayer.playMovies(); System.out.println("============分割线"); } private static void useAdapterOfInterFace() { // 接口适配模式测试 == 适配类无需实现接口里面的所有方法,这时候需要借助一个抽象类进行一下过度 AbstractPlayer aPlayer = new APlayer(); aPlayer.show(); AbstractPlayer bPlayer = new BPlayer(); bPlayer.show(); AbstractPlayer cPlayer = new CPlayer(); cPlayer.show(); } }
实际场景
- 场景描述:将200V的交流电适配成5V的直流电用于给手机充电
- Coding:
220V的交流电
public class AC220 { public int outputAC220V(){ int output = 220; System.out.println("输出交流电"+output+"V"); return output; } }
5V的直流电接口
public interface DC5 { int outputDC5V(); }
适配器
public class PowerAdapter implements DC5{ private AC220 ac220 = new AC220(); @Override public int outputDC5V() { int adapterInput = ac220.outputAC220V(); // 变压器... int adapterOutput = adapterInput / 44; System.out.println("使用PowerAdapter输入AC:" + adapterInput + "V" + "输出DC:" + adapterOutput + "V"); return adapterOutput; } }
测试类
public class Test { public static void main(String[] args) { DC5 dc5 = new PowerAdapter(); dc5.outputDC5V(); } }
打印结果
输出交流电220V
使用PowerAdapter输入AC:220V输出DC:5V
源码中应用
- javax.xml.bind.annotation.adapters.XmlAdapter
- org.springframework.aop.framework.adapter.AdvisorAdapter:Spring 源码中 AOP 相关的适配器,可以看到它的一个实现
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { MethodBeforeAdviceAdapter() { } public boolean supportsAdvice(Advice advice) { return advice instanceof MethodBeforeAdvice; } public MethodInterceptor getInterceptor(Advisor advisor) { MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice(); return new MethodBeforeAdviceInterceptor(advice); } }
标签:适配器,接口,public,模式,播放,void,Mp3 From: https://www.cnblogs.com/wangzhilei-src/p/17970035