适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。适配器模式有三种:类适配器、对象适配器、接口适配器
适配器解决的问题
主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。
命令模式模式角色
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
适配器模式和装饰着模式的区别
适配器模式的意义是要将一个接口转变成另一个接口,它的目的是通过改变接口来达到重复使用的目的。而装饰器模式不是要改变被装饰对象的接口,而是恰恰要保持原有的接口,但是增强原有对象的功能,或者改变原有对象的处理方式而提升性能。
代码实现:220v电压适配成5v电压
/** * 目标(Target)接口 */ public interface Targetable { public int v5(); }
/** * 适配者(Adaptee)类 */ public class Adaptee { public int v220(){ return 220; } }
1.基于对象的适配器,组合适配者对象
/** * * 采用对象聚合的方式进行适配 * * 对象的适配器模式 * */ public class ObjectAdapterMode implements Targetable { private Adaptee adaptee; public ObjectAdapterMode(Adaptee adaptee) { this.adaptee = adaptee; } public int v5() { return adaptee.v220()/44; } }
2.基于类的适配器,继承适配者类
public class ClassAdapterMode extends Adaptee implements Targetable { public int v5() { return super.v220() / 44; } }
3.基于接口的适配器,同时实现目标接口和适配者接口
public interface Adapteeable { public int v220(); } public class InterfaceAdapterMode implements Targetable ,Adapteeable{ public int v220() { return 220; } public int v5() { return v220()/44; } }
JDK中的适配器模式
public class InputStreamReader extends Reader { private final StreamDecoder sd; /** * Creates an InputStreamReader that uses the default charset. * * @param in An InputStream */ public InputStreamReader(InputStream in) { super(in); try { sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object } catch (UnsupportedEncodingException e) { // The default encoding should always be available throw new Error(e); } } /** * Creates an InputStreamReader that uses the named charset. * * @param in * An InputStream * * @param charsetName * The name of a supported * {@link java.nio.charset.Charset charset} * * @exception UnsupportedEncodingException * If the named charset is not supported */ public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException { super(in); if (charsetName == null) throw new NullPointerException("charsetName"); sd = StreamDecoder.forInputStreamReader(in, this, charsetName); } /** * Creates an InputStreamReader that uses the given charset. * * @param in An InputStream * @param cs A charset * * @since 1.4 * @spec JSR-51 */ public InputStreamReader(InputStream in, Charset cs) { super(in); if (cs == null) throw new NullPointerException("charset"); sd = StreamDecoder.forInputStreamReader(in, this, cs); } /** * Creates an InputStreamReader that uses the given charset decoder. * * @param in An InputStream * @param dec A charset decoder * * @since 1.4 * @spec JSR-51 */ public InputStreamReader(InputStream in, CharsetDecoder dec) { super(in); if (dec == null) throw new NullPointerException("charset decoder"); sd = StreamDecoder.forInputStreamReader(in, this, dec); } /** * Returns the name of the character encoding being used by this stream. * * <p> If the encoding has an historical name then that name is returned; * otherwise the encoding's canonical name is returned. * * <p> If this instance was created with the {@link * #InputStreamReader(InputStream, String)} constructor then the returned * name, being unique for the encoding, may differ from the name passed to * the constructor. This method will return <code>null</code> if the * stream has been closed. * </p> * @return The historical name of this encoding, or * <code>null</code> if the stream has been closed * * @see java.nio.charset.Charset * * @revised 1.4 * @spec JSR-51 */ public String getEncoding() { return sd.getEncoding(); } /** * Reads a single character. * * @return The character read, or -1 if the end of the stream has been * reached * * @exception IOException If an I/O error occurs */ public int read() throws IOException { return sd.read(); } /** * Reads characters into a portion of an array. * * @param cbuf Destination buffer * @param offset Offset at which to start storing characters * @param length Maximum number of characters to read * * @return The number of characters read, or -1 if the end of the * stream has been reached * * @exception IOException If an I/O error occurs */ public int read(char cbuf[], int offset, int length) throws IOException { return sd.read(cbuf, offset, length); } /** * Tells whether this stream is ready to be read. An InputStreamReader is * ready if its input buffer is not empty, or if bytes are available to be * read from the underlying byte stream. * * @exception IOException If an I/O error occurs */ public boolean ready() throws IOException { return sd.ready(); } public void close() throws IOException { sd.close(); } }
InputStreamReader是通过对象适配的方式进行适配,Reader是目标,StreamDecoder是适配者。
优缺点
优点:可以让任何两个没有关联的类一起运行,提高了类的复用,增加了类的透明度,灵活性好。
缺点:过多地使用适配器,会让系统非常零乱,维护成本高。
生活中的命令模式
中国标准输出电压220v,手机充电器5v电压,无法改变标准输出,可以增加一个充电插头,它负责将220v的电压转化成5v电压,充当了一个适配器的作用。
手机内存读卡器,将内存卡转换成USB接口,方便笔记本电脑读取手机内存卡中的内容。手机圆孔耳机和方块耳机,可以通过转换器进行转换。
每个人都有不同的角色,在公司是员工,在学校是学生,在家里是家庭成员等等,进入不同的环境,我们要切换不同的角色,与之适配。
我的启发
适配器模式,大丈夫能屈能伸,在艰苦的环境下,坚韧不拔。在舒适的环境,居安思危。做人做事,外圆内方,精彩塑造自己的每一个角色。