适配器模式
定义
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
结构
适配器模式(Adapter)包含以下主要角色:
- 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
类适配器模式
示例
定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
代码
1 2 3 4 5
| public interface TFCard { String read();
void write(String msg); }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class TFCardImpl implements TFCard{ @Override public String read() { System.out.println("TFCard读取信息======》读出周杰伦的专辑"); return null; }
@Override public void write(String msg) { System.out.println("TFCard写信息"+msg+"到磁盘中"); } }
|
1 2 3 4
| public interface SDCard { String readSD(); void writeSD(String msg); }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class SDCardImpl implements SDCard{ @Override public String readSD() { System.out.println("SD 读取信息===》读出周杰伦的专辑"); return null; }
@Override public void writeSD(String msg) { System.out.println("SD卡写入磁盘===》"+msg); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class SDAdapterTF extends TFCardImpl implements SDCard{ @Override public String readSD() { super.read(); return null; }
@Override public void writeSD(String msg) { super.write(msg); } }
|
1 2 3 4 5 6 7 8 9 10
| public class Computer { public String readSD(SDCard sdCard){ sdCard.readSD(); return null; }
public void write(SDCard sdCard,String msg){ sdCard.writeSD(msg); } }
|
测试
缺点
类适配器模式违背了合成复用原则。类适配器是客户类有一个接口规范的情况下可用,反之不可用
对象适配器模式
示例
实现方式:对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。
代码
对比类适配方式,对象适配器只需要修改转换器即可,该类与之前不同,只需要将转换类公共接口聚合到转换器中就可以,实现了类的解耦合,并且在客户类没有实现共同接口时也可以直接使用继承客户类,重写其方法即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class SDAdapterTF implements SDCard {
private TFCard tfCard;
public SDAdapterTF(TFCard tfCard){ this.tfCard = tfCard; }
@Override public String readSD() { tfCard.read(); return null; }
@Override public void writeSD(String msg) { tfCard.write(msg); } }
|
测试
应用场景
- 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
- 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
JDK源码
Reader(字符流)、InputStream(字节流)的适配使用的是InputStreamReader。
类图
源码
InputStreamReader继承自java.io包中的Reader,对他中的抽象的未实现的方法给出实现。如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| public class InputStreamReader extends Reader {
private final StreamDecoder sd;
public InputStreamReader(InputStream in) { super(in); try { sd = StreamDecoder.forInputStreamReader(in, this, (String)null); } catch (UnsupportedEncodingException e) { throw new Error(e); } }
public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException { super(in); if (charsetName == null) throw new NullPointerException("charsetName"); sd = StreamDecoder.forInputStreamReader(in, this, charsetName); }
public InputStreamReader(InputStream in, Charset cs) { super(in); if (cs == null) throw new NullPointerException("charset"); sd = StreamDecoder.forInputStreamReader(in, this, cs); }
public InputStreamReader(InputStream in, CharsetDecoder dec) { super(in); if (dec == null) throw new NullPointerException("charset decoder"); sd = StreamDecoder.forInputStreamReader(in, this, dec); }
public String getEncoding() { return sd.getEncoding(); }
public int read() throws IOException { return sd.read(); }
public int read(char cbuf[], int offset, int length) throws IOException { return sd.read(cbuf, offset, length); }
public boolean ready() throws IOException { return sd.ready(); }
public void close() throws IOException { sd.close(); } }
|
从源码可以发现,该类继承了Reader类,并且聚合了StreamDecoder类,在构造器中传入字节输入流,此时调用StreamDecoder的静态方法,将其转换为StreamDecoder类,然后调用StreamDecoder的读方法。