一个最纯粹的技术分享网站,打造精品技术编程专栏!编程进阶网
适配器模式是一种结构型设计模式,用于将不兼容的接口转换为可兼容的接口,使原本不能一起工作的类可以协同工作。本文详细介绍了适配器模式的基础、实现方式(类适配器和对象适配器)、应用场景(如封装有缺陷的接口、统一多个类的接口、替换依赖的外部系统等)以及优缺点。通过具体案例(如读卡器适配、播放器适配)和实际开发中的应用(如数据库适配器、日志框架适配器),帮助读者深入理解和应用适配器模式。
适配器模式的英文翻译是 Adapter Design Pattern。
顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。
对于这个模式,有一个经常被拿来解释它的例子,就是 USB 转接头充当适配器,把两种不兼容的接口,通过转接变得可以一起工作。
主要解决问题:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
简单来说,用于事后补救措施,项目代码后期,想让不想关的类,变成可以一起工作。
适配器模式(Adapter Pattern)的定义 :
在计算机编程中,当我们有两个已有的功能,但由于某种原因它们不能直接协同工作时,我们使用适配器模式。
举个例子1:类似USB转接头,既可以链接Android手机,也可以链接IOS手机。
举个例子2:欧洲制造的电器,但你住在中国。你不能直接将这部电器插入墙上,因为插头和插座不兼容。此时,你需要一个适配器,将欧洲插头转换为中国插座。
适配器模式应该在真正需要时使用,而不是为了强行适配而使用。
在设计阶段,应该尽量考虑接口的一致性和兼容性,以减少对适配器模式的依赖。
适配器模式在软件开发中有多种应用场景,以下是一些常见的应用场景:
适配器有两种方式:
具体的代码实现如下所示。
其中,ITarget 表示要转化成的接口定义。Adaptee 是一组不兼容 ITarget 接口定义的接口,Adaptor 将 Adaptee 转化成一组符合 ITarget 接口定义的接口。
类的适配器。通过继承来实现
●目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。
●源(Adapee)角色:现在需要适配的接口。
●适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
// 类适配器: 基于继承
public interface ITarget {
void f1();
void f2();
void f3();
}
public class Adaptee {
public void fa() {
//...
}
public void fb() {
//...
}
public void fc() {
//...
}
}
public class Adaptor extends Adaptee implements ITarget {
public void f1() {
super.fa();
System.out.println("调用适配器中方法");
}
public void f2() {
//...重新实现f2()...
super.fb();
System.out.println("重新实现f2");
}
// 这里fc()不需要实现,直接继承自Adaptee,这是跟对象适配器最大的不同点
@Override
public void f3() {
}
}
对象的适配器。通过组合来实现
// 对象适配器:基于组合
public interface ITarget {
void f1();
void f2();
void fc();
}
public class Adaptee {
public void fa() {
//...
}
public void fb() {
//...
}
public void fc() {
//...
}
}
public class Adaptor implements ITarget {
private final Adaptee adaptee;
public Adaptor(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void f1() {
adaptee.fa(); //委托给Adaptee
}
public void f2() {
//...重新实现f2()...
}
public void fc() {
adaptee.fc();
}
}
针对这两种实现方式,在实际的开发中,到底该如何选择使用哪一种呢?
判断的标准主要有两个,一个是 Adaptee 接口的个数,另一个是 Adaptee 和 ITarget 的契合程度。
类适配器是一种结构型设计模式,它允许将一个类的接口转换为另一个客户端所期望的接口。通过继承原始类和实现目标接口,类适配器使得原始类的接口与目标接口兼容。
特点: 1.使用继承:类适配器通过继承源类来实现适配功能。2.单一适配:由于 Java 中不支持多重继承,类适配器只能适配一个源类。
对象适配器是适配器模式的一种变体,它通过组合(而不是继承)原始类和目标接口来实现适配器功能。在对象适配器中,适配器类持有一个原始类的实例,并实现目标接口,通过调用原始类的方法来实现目标接口的方法。
特点:1.适配器通过组合原始类的实例来实现适配器功能,而不是通过继承原始类。2.对象适配器可以重用现有的功能,无需修改原始类的代码。
适配器模式包含如下角色:
适配器模式时序图如下所示:
读卡器需求分析:
现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将TF卡中的内容读取出来。
首先我们先开发电脑读取SD卡的业务:
Computer只能使用SD卡,所以方法名为readSD(),需要SDCard类型的对象。具体做法是:主要是SDCard(包含两个方法:读数据,写数据),子实现类为SDCardImpl。
public interface SDCard {
//从SD卡中读取数据
String readSD();
//往SD卡中写数据
void writeSD(String msg);
}
//具体的SD卡
public class SDCardImpl implements SDCard {
@Override
public String readSD() {
String msg = "SDCard read msg : SD";
System.out.println(msg);
return msg;
}
@Override
public void writeSD(String msg) {
System.out.println("SDCard write msg : " + msg);
}
}
//计算机类
public class Computer {
//从SD卡中读取数据
public String readSD(SDCard sdCard) {
if (sdCard == null) {
throw new NullPointerException("sd card is null");
}
return sdCard.readSD();
}
}
private void test1() {
//计算机读SD卡
Computer computer = new Computer();
SDCardImpl sdCard = new SDCardImpl();
computer.readSD(sdCard);
}
//输出结果是:
//SDCard read msg : SD
然后,思想一下如何给TF做适配。
//适配者类的接口
public interface TFCard {
//从TF卡中读取数据
String readTF();
//往TF卡中写数据
void writeTF(String msg);
}
//适配者类
public class TFCardImpl implements TFCard {
@Override
public String readTF() {
String msg = "TFCard read msg : TF";
System.out.println(msg);
return msg;
}
@Override
public void writeTF(String msg) {
System.out.println("TFCard write msg : " + msg);
}
}
//适配器类
public class SDAdapterTF extends TFCardImpl implements SDCard {
@Override
public String readSD() {
System.out.println("adapter read tf card");
return readTF();
}
@Override
public void writeSD(String msg) {
System.out.println("adapter wrete tf card");
writeTF(msg);
}
}
private void test2() {
//计算器适配,读卡器,去读TF卡中的内容
Computer computer = new Computer();
//创建适配器对象,完全没有影响之前计算机读SD卡的逻辑
SDAdapterTF sdAdapterTF = new SDAdapterTF();
computer.readSD(sdAdapterTF);
}
通过这个案例可以知道
适配器模式是一种强大的设计模式,能够有效解决接口不兼容的问题,使得不同接口的类能够协同工作。通过合理使用适配器模式,可以提高系统的灵活性和复用性,但也需要注意其可能带来的复杂性和性能影响。
优点
缺点
播放器案例需求分析
想象一下,我们有一个MediaPlayer接口,它可以播放mp3格式的文件。
private void test1() {
MediaPlayer mp3Player = new Mp3Player();
mp3Player.play("mp3","file.mp3");
}
// MediaPlayer.java
public interface MediaPlayer {
void play(String audioType, String fileName);
}
// Mp3Player.java
public class Mp3Player implements MediaPlayer {
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file. Name: " + fileName);
} else {
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
现在,我们想扩展这个功能,使其也可以播放其他格式的文件,比如vlc和mp4。但是,我们不希望修改原始的MediaPlayer接口。
为了支持更多的格式,我们将创建一个新的接口AdvancedMediaPlayer:
private void test2() {
AdvancedMediaPlayer vlcPlayer = new VlcPlayer();
vlcPlayer.playVlc("vlc");
AdvancedMediaPlayer mp4Player = new Mp4Player();
mp4Player.playMp4("mp4");
}
// AdvancedMediaPlayer.java
public interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
// VlcPlayer.java
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: " + fileName);
}
@Override
public void playMp4(String fileName) {
// Do nothing
}
}
// Mp4Player.java
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// Do nothing
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: " + fileName);
}
}
现在,我们已经有了基础的播放器,但我们还需要一个适配器,以便MediaPlayer可以使用AdvancedMediaPlayer。
实现MediaPlayer适配器。为了使MediaPlayer能够使用AdvancedMediaPlayer,我们需要创建一个适配器。这个适配器将决定使用哪个AdvancedMediaPlayer的实现。
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
现在,让我们扩展我们的Mp3Player类,使其可以使用MediaAdapter来播放其他格式:
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file. Name: " + fileName);
} else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
让我们通过一些测试来验证我们的适配器是否正常工作:
private void test3() {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "mySong.mp3");
audioPlayer.play("mp4", "video.mp4");
audioPlayer.play("vlc", "movie.vlc");
audioPlayer.play("avi", "doubi.avi"); // Not supported
}
适配器模式是一个非常有用的模式,它允许我们整合不兼容的接口,而不需要修改原始代码。在本例中,我们成功地扩展了MediaPlayer的功能,使其可以播放其他格式的文件,而不需要改变其原始定义。
像上面的MediaPlayer示例一样,你可能会在许多真实的应用程序中遇到类似的场景,其中一些旧的接口需要与新的接口一起工作,但又不希望进行大规模的重写。
对象适配器模式具有以下优点:
然而,对象适配器模式也有一些缺点和限制:
大概有哪些场景
为何用这个
一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。应用这种模式算是“无奈之举”。如果在设计初期,我们就能协调规避接口不兼容的问题,那这种模式就没有应用的机会了。
假设我们依赖的外部系统在接口设计方面有缺陷(比如包含大量静态方法),引入之后会影响到我们自身代码的可测试性。
为了隔离设计上的缺陷,我们希望对外部系统提供的接口进行二次封装,抽象出更好的接口设计,这个时候就可以使用适配器模式了。
具体我还是举个例子来解释一下,你直接看代码应该会更清晰。具体代码如下所示:
public class CD { //这个类来自外部sdk,我们无权修改它的代码
//...
public static void staticFunction1() { //... }
public void uglyNamingFunction2() { //... }
public void tooManyParamsFunction3(int paramA, int paramB, ...) { //... }
public void lowPerformanceFunction4() { //... }
}
// 使用适配器模式进行重构
public class ITarget {
void function1();
void function2();
void fucntion3(ParamsWrapperDefinition paramsWrapper);
void function4();
//...
}
// 注意:适配器类的命名不一定非得末尾带Adaptor
public class CDAdaptor extends CD implements ITarget {
//...
public void function1() {
super.staticFunction1();
}
public void function2() {
super.uglyNamingFucntion2();
}
public void function3(ParamsWrapperDefinition paramsWrapper) {
super.tooManyParamsFunction3(paramsWrapper.getParamA(), ...);
}
public void function4() {
//...reimplement it...
}
}
某个功能的实现依赖多个外部系统(或者说类)。通过适配器模式,将它们的接口适配为统一的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。具体我还是举个例子来解释一下。
假设我们的系统要对用户输入的文本内容做敏感词过滤,为了提高过滤的召回率,我们引入了多款第三方敏感词过滤系统,依次对用户输入的内容进行过滤,过滤掉尽可能多的敏感词。
但是,每个系统提供的过滤接口都是不同的。这就意味着我们没法复用一套逻辑来调用各个系统。这个时候,我们就可以使用适配器模式,将所有系统的接口适配为统一的接口定义,这样我们可以复用调用敏感词过滤的代码。
你可以配合着下面的代码示例,来理解我刚才举的这个例子。
public class ASensitiveWordsFilter { // A敏感词过滤系统提供的接口
//text是原始文本,函数输出用***替换敏感词之后的文本
public String filterSexyWords(String text) {
// ...
}
public String filterPoliticalWords(String text) {
// ...
}
}
public class BSensitiveWordsFilter { // B敏感词过滤系统提供的接口
public String filter(String text) {
//...
}
}
public class CSensitiveWordsFilter { // C敏感词过滤系统提供的接口
public String filter(String text, String mask) {
//...
}
}
// 未使用适配器模式之前的代码:代码的可测试性、扩展性不好
public class RiskManagement {
private ASensitiveWordsFilter aFilter = new ASensitiveWordsFilter();
private BSensitiveWordsFilter bFilter = new BSensitiveWordsFilter();
private CSensitiveWordsFilter cFilter = new CSensitiveWordsFilter();
public String filterSensitiveWords(String text) {
String maskedText = aFilter.filterSexyWords(text);
maskedText = aFilter.filterPoliticalWords(maskedText);
maskedText = bFilter.filter(maskedText);
maskedText = cFilter.filter(maskedText, "***");
return maskedText;
}
}
// 使用适配器模式进行改造
public interface ISensitiveWordsFilter { // 统一接口定义
String filter(String text);
}
public class ASensitiveWordsFilterAdaptor implements ISensitiveWordsFilter {
private ASensitiveWordsFilter aFilter;
public String filter(String text) {
String maskedText = aFilter.filterSexyWords(text);
maskedText = aFilter.filterPoliticalWords(maskedText);
return maskedText;
}
}
//...省略BSensitiveWordsFilterAdaptor、CSensitiveWordsFilterAdaptor...
// 扩展性更好,更加符合开闭原则,如果添加一个新的敏感词过滤系统,
// 这个类完全不需要改动;而且基于接口而非实现编程,代码的可测试性更好。
public class RiskManagement {
private List<ISensitiveWordsFilter> filters = new ArrayList<>();
public void addSensitiveWordsFilter(ISensitiveWordsFilter filter) {
filters.add(filter);
}
public String filterSensitiveWords(String text) {
String maskedText = text;
for (ISensitiveWordsFilter filter : filters) {
maskedText = filter.filter(maskedText);
}
return maskedText;
}
}
当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以减少对代码的改动。具体的代码示例如下所示:
// 外部系统A
public interface IA {
//...
void fa();
}
public class A implements IA {
//...
public void fa() { //... }
}
// 在我们的项目中,外部系统A的使用示例
public class Demo {
private IA a;
public Demo(IA a) {
this.a = a;
}
//...
}
Demo d = new Demo(new A());
// 将外部系统A替换成外部系统B
public class BAdaptor implemnts IA {
private B b;
public BAdaptor(B b) {
this.b= b;
}
public void fa() {
//...
b.fb();
}
}
// 借助BAdaptor,Demo的代码中,调用IA接口的地方都无需改动,
// 只需要将BAdaptor如下注入到Demo即可。
Demo d = new Demo(new BAdaptor(new B()));
在做版本升级的时候,对于一些要废弃的接口,我们不直接将其删除,而是暂时保留,并且标注为 deprecated,并将内部实现逻辑委托为新的接口实现。这样做的好处是,让使用它的项目有个过渡期,而不是强制进行代码修改。这也可以粗略地看作适配器模式的一个应用场景。同样,我还是通过一个例子,来进一步解释一下。
JDK1.0 中包含一个遍历集合容器的类 Enumeration。JDK2.0 对这个类进行了重构,将它改名为 Iterator 类,并且对它的代码实现做了优化。但是考虑到如果将 Enumeration 直接从 JDK2.0 中删除,那使用 JDK1.0 的项目如果切换到 JDK2.0,代码就会编译不通过。为了避免这种情况的发生,我们必须把项目中所有使用到 Enumeration 的地方,都修改为使用 Iterator 才行。
单独一个项目做 Enumeration 到 Iterator 的替换,勉强还能接受。但是,使用 Java 开发的项目太多了,一次 JDK 的升级,导致所有的项目不做代码修改就会编译报错,这显然是不合理的。这就是我们经常所说的不兼容升级。为了做到兼容使用低版本 JDK 的老代码,我们可以暂时保留 Enumeration 类,并将其实现替换为直接调用 Itertor。代码示例如下所示:
public class Collections {
public static Emueration emumeration(final Collection c) {
return new Enumeration() {
Iterator i = c.iterator();
public boolean hasMoreElments() {
return i.hashNext();
}
public Object nextElement() {
return i.next():
}
}
}
}
前面我们讲到,适配器模式主要用于接口的适配,实际上,它还可以用在不同格式的数据之间的适配。
比如,把从不同征信系统拉取的不同格式的征信数据,统一为相同的格式,以方便存储和使用。
再比如,Java 中的 Arrays.asList() 也可以看作一种数据适配器,将数组类型的数据转化为集合容器类型。
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
JDK1.1 之前提供的容器有 Arrays,Vector,Stack,Hashtable,Properties,BitSet,其中定义了一种访问群集内各元素的标准方式,称为 Enumeration(列举器)接口。
Vector v=new Vector();
for (Enumeration enum =v.elements(); enum.hasMoreElements();) {
Object o = enum.nextElement();
processObject(o);
}
JDK1.2 版本中引入了 Iterator 接口,新版本的集合对(HashSet,HashMap,WeakHashMap,ArrayList,TreeSet,TreeMap, LinkedList)是通过 Iterator 接口访问集合元素。
List list=new ArrayList();
for(Iterator it=list.iterator();it.hasNext();){
System.out.println(it.next());
}
这样,如果将老版本的程序运行在新的 Java 编译器上就会出错。因为 List 接口中已经没有 elements(),而只有 iterator() 了。
那么如何将老版本的程序运行在新的 Java 编译器上呢? 如果不加修改,是肯定不行的,但是修改要遵循“开-闭”原则。我们可以用 Java 设计模式中的适配器模式解决这个问题。
public class NewEnumeration implements Enumeration {
Iterator it;
public NewEnumeration(Iterator it) {
this.it = it;
}
public boolean hasMoreElements() {
return it.hasNext();
}
public Object nextElement() {
return it.next();
}
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("C");
for (Enumeration e = new NewEnumeration(list.iterator()); e.hasMoreElements(); ) {
System.out.println(e.nextElement());
}
}
}
NewEnumeration 是一个适配器类,通过它实现了从 Iterator 接口到 Enumeration 接口的适配,这样我们就可以使用老版本的代码来使用新的集合对象了。
在许多应用程序中,需要与多种数据库进行交互。如果每种数据库都有不同的接口,那么使用适配器模式可以简化代码,并提高代码的可维护性和可扩展性。例如:
public interface Database {
void connect();
void query(String sql);
}
public class MySqlDatabase implements Database {
@Override
public void connect() {
System.out.println("Connecting to MySQL Database");
}
@Override
public void query(String sql) {
System.out.println("Querying data from MySQL Database: " + sql);
}
}
public class OracleDatabaseAdapter implements Database {
private OracleDatabase oracleDatabase;
public OracleDatabaseAdapter(OracleDatabase oracleDatabase) {
this.oracleDatabase = oracleDatabase;
}
@Override
public void connect() {
oracleDatabase.open();
}
@Override
public void query(String sql) {
oracleDatabase.execute(sql);
}
}
class OracleDatabase {
void open() {
System.out.println("Opening Oracle Database");
}
void execute(String sql) {
System.out.println("Executing SQL on Oracle Database: " + sql);
}
}
许多Java应用程序使用日志框架来记录应用程序的运行情况。有许多不同的日志框架,比如Log4j、SLF4J等,它们有着不同的API。通过使用适配器模式,我们可以定义一个统一的日志接口,然后为每个日志框架实现一个适配器,从而让应用程序可以在不同的日志框架之间无缝切换。
public interface Logger {
void log(String message);
}
public class Log4jAdapter implements Logger {
private org.apache.log4j.Logger logger;
public Log4jAdapter(org.apache.log4j.Logger logger) {
this.logger = logger;
}
@Override
public void log(String message) {
logger.info(message);
}
}
public class SLF4JAdapter implements Logger {
private org.slf4j.Logger logger;
public SLF4JAdapter(org.slf4j.Logger logger) {
this.logger = logger;
}
@Override
public void log(String message) {
logger.info(message);
}
}
这样,我们就可以在应用程序中自由切换使用哪个日志框架,而不需要修改大量的代码。
在实际开发中,我们经常会用到第三方库。但是,不同的库可能提供了不同的API,直接使用会导致代码的耦合度增加。通过适配器模式,我们可以为每个库提供一个适配器,使它们都符合同一个接口,这样在主程序中就可以无缝切换,降低了代码的耦合度。
在以下情况下可以使用适配器模式:
认适配器模式(Default Adapter Pattern)或缺省适配器模式
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。因此也称为单接口适配器模式。
一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。
应用这种模式算是“无奈之举”,如果在设计初期,我们就能协调规避接口不兼容的问题,那这种模式就没有应用的机会了。
代理、桥接、装饰器、适配器,这 4 种模式是比较常用的结构型设计模式。它们的代码结构非常相似。
尽管代码结构相似,但这4种设计模式的用意完全不同,也就是说要解决的问题、应用场景不同,这也是它们的主要区别。这里我就简单说一下它们之间的区别。
01.适配器模式基础
什么叫做适配器模式?用来做适配,将不兼容的接口转化成兼容的接口,例子就是 USB 转接头充当适配器,把两种不兼容的接口,通过转接变得可以一起工作。
简单来说,用于事后补救措施,项目代码后期,想让不想关的类,变成可以一起工作。可以作为类结构型模式,也可以作为对象结构型模式。
适配器模式思考?不要强行适配,设计前期就要考虑到兼容性,以减少对适配器依赖!
02.适配器模式实现
适配器模式两种实现方式:
03.适配器模式分析
类适配器是一种结构型设计模式,它允许将一个类的接口转换为另一个客户端所期望的接口。通过继承原始类和实现目标接口,类适配器使得原始类的接口与目标接口兼容。
对象适配器是适配器模式的一种变体,它通过组合(而不是继承)原始类和目标接口来实现适配器功能。在对象适配器中,适配器类持有一个原始类的实例,并实现目标接口,通过调用原始类的方法来实现目标接口的方法。
04.适配器应用解析
类适配器:现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将TF卡中的内容读取出来。
对象适配器:有一个MediaPlayer接口,它可以播放mp3格式的文件。现在,我们想扩展这个功能,使其也可以播放其他格式的文件,比如vlc和mp4。
05.应用场景分析
一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。应用这种模式算是“无奈之举”。如果在设计初期,我们就能协调规避接口不兼容的问题,那这种模式就没有应用的机会了。
模块 | 描述 | 备注 |
---|---|---|
GitHub | 多个YC系列开源项目,包含Android组件库,以及多个案例 | |
博客汇总 | 汇聚Java,Android,C/C++,网络协议,算法,编程总结等 | |
设计模式 | 六大设计原则,23种设计模式,设计模式案例,面向对象思想 | |
Java进阶 | 数据设计和原理,面向对象核心思想,IO,异常,线程和并发,JVM | |
网络协议 | 网络实际案例,网络原理和分层,Https,网络请求,故障排查 | |
计算机原理 | 计算机组成结构,框架,存储器,CPU设计,内存设计,指令编程原理,异常处理机制,IO操作和原理 | |
学习C编程 | C语言入门级别系统全面的学习教程,学习三到四个综合案例 | |
C++编程 | C++语言入门级别系统全面的教学教程,并发编程,核心原理 | |
算法实践 | 专栏,数组,链表,栈,队列,树,哈希,递归,查找,排序等 | |
Android | 基础入门,开源库解读,性能优化,Framework,方案设计 |
23种设计模式
23种设计模式 & 描述 & 核心作用 | 包括 |
---|---|
创建型模式 提供创建对象用例。能够将软件模块中对象的创建和对象的使用分离 | 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
结构型模式 关注类和对象的组合。描述如何将类或者对象结合在一起形成更大的结构 | 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
行为型模式 特别关注对象之间的通信。主要解决的就是“类或对象之间的交互”问题 | 责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。