`
gaojingsong
  • 浏览: 1155004 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

设计模式之适配器(变压器)模式

阅读更多

适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。

 

 

中国市电220V,如果买个国产笔记本去国外,国外电压110V如何使用呢?幸好有了笔记本电源适配器帮忙

/** 

* Created by IntelliJ IDEA. 

* Date: 2008-8-2 14:43:46 

* 源角色 

*/ 

public class Adaptee { 

    public int get220v(){ 

       return 220; 

    } 

}

 

/** 

* Created by IntelliJ IDEA. 

* Date: 2008-8-2 14:43:23 

* 目标角色 

*/ 

public interface Target { 

    int get110v(); 

    int get220v(); 

}

 

/** 

* Created by IntelliJ IDEA. 

* Date: 2008-8-2 14:43:07 

* 适配器角色:扩展源角色,实现目标角色,从而使得目标角色改动时候,不用改动源角色,只要改动适配器 

*/ 

public class Adapter extends Adaptee implements Target{ 

    public int get110v(){ 

        return 110; 

    } 

}

 

/** 

* Created by IntelliJ IDEA. 

* Date: 2008-8-2 15:00:31 

* 客户端 

*/ 

public class Client { 

    public static void main(String rags[]) { 

        new Client().test(); 

    } 

 

    public void test() { 

        Target target = new Adapter(); 

        int v1 = target.get110v(); 

        int v2 = target.get220v(); 

    } 

}

 

适配器通过扩展源角色,同时实现目标接口,从而满足了同时提供220v,110v电压的要求。

 

 

 

 

 

鲁智深的故事

  和尚要做什么呢?吃斋、念经、打坐、撞钟、习武等。如果设计一个和尚接口,给出所有的和尚都需要实现的方法,那么这个接口应当如下:

public interface 和尚 {

    public void 吃斋();

    public void 念经();

    public void 打坐();

    public void 撞钟();

    public void 习武();

    public String getName();

}

显然,所有的和尚类都应当实现接口所定义的全部方法,不然就根本通不过JAVA语言编辑器。像下面的鲁智深类就不行。

public class 鲁智深 implements 和尚{

    public void 习武(){

        拳打镇关西;

        大闹五台山;

        大闹桃花村;

        火烧瓦官寺;

        倒拔垂杨柳;

    }

    public String getName(){

        return "鲁智深";

    }

}

 

由于鲁智深只实现了getName()和习武()方法,而没有实现任何其他的方法。因此,它根本就通不过Java语言编译器。鲁智深类只有实现和尚接口的所有的方法才可以通过Java语言编译器,但是这样一来鲁智深就不再是鲁智深了。以史为鉴,可以知天下。研究一下几百年前鲁智深是怎么剃度成和尚的,会对Java编程有很大的启发。不错,当初鲁达剃度,众僧说:“此人形容丑恶、相貌凶顽,不可剃度他",但是长老却说:”此人上应天星、心地刚直。虽然时下凶顽,命中驳杂,久后却得清净。证果非凡,汝等皆不及他。”

原来如此!看来只要这里也应上一个天星的话,问题就解决了!使用面向对象的语言来说,“应”者,实现也;“天星”者,抽象类也。

 

public abstract class 天星 implements 和尚 {

    public void 吃斋(){}

    public void 念经(){}

    public void 打坐(){}

    public void 撞钟(){}

    public void 习武(){}

    public String getName(){

        return null;

    }

}

 

鲁智深类继承抽象类“天星”

public class 鲁智深 extends 天星{

    public void 习武(){

        拳打镇关西;

        大闹五台山;

        大闹桃花村;

        火烧瓦官寺;

        倒拔垂杨柳;

    }

    public String getName(){

        return "鲁智深";

    }

}

 

这个抽象的天星类便是一个适配器类,鲁智深实际上借助于适配器模式达到了剃度的目的。此适配器类实现了和尚接口所要求的所有方法。但是与通常的适配器模式不同的是,此适配器类给出的所有的方法的实现都是“平庸”的。这种“平庸化”的适配器模式称作缺省适配模式。

在很多情况下,必须让一个具体类实现某一个接口,但是这个类又用不到接口所规定的所有的方法。通常的处理方法是,这个具体类要实现所有的方法,那些有用的方法要有实现,那些没有用的方法也要有空的、平庸的实现。

这些空的方法是一种浪费,有时也是一种混乱。除非看过这些空方法的代码,程序员可能会以为这些方法不是空的。即便他知道其中有一些方法是空的,也不一定知道哪些方法是空的,哪些方法不是空的,除非看过这些方法的源代码或是文档。

缺省适配模式可以很好的处理这一情况。可以设计一个抽象的适配器类实现接口,此抽象类要给接口所要求的每一种方法都提供一个空的方法。就像帮助了鲁智深的“上应天星”一样,此抽象类可以使它的具体子类免于被迫实现空的方法。

 

 

 

JDK 中有大量使用适配器模式的案例,清单 11 大致列举了一些类。

清单 11. 使用适配器模式的类

java.util.Arrays#asList()

javax.swing.JTable(TableModel)

java.io.InputStreamReader(InputStream)

java.io.OutputStreamWriter(OutputStream)

javax.xml.bind.annotation.adapters.XmlAdapter#marshal()

javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()

JDK1.1 之前提供的容器有 Arrays,Vector,Stack,Hashtable,Properties,BitSet,其中定义了一种访问群集内各元素的标准方式,称为 Enumeration(列举器)接口,用法如清单 12 所示。

清单 12.Enumeration 接口实现方式

Vector v=new Vector();

for (Enumeration enum =v.elements(); enum.hasMoreElements();) {

Object o = enum.nextElement();

processObject(o);

}

JDK1.2 版本中引入了 Iterator 接口,新版本的集合对象(HashSet,HashMap,WeakHeahMap,ArrayList,TreeSet,TreeMap, LinkedList)是通过 Iterator 接口访问集合元素的,用法如清单 13 所示。

清单 13. Iterator 接口实现方式

List list=new ArrayList();

for(Iterator it=list.iterator();it.hasNext();)

{

 System.out.println(it.next());

}

这样,如果将老版本的程序运行在新的 Java 编译器上就会出错。因为 List 接口中已经没有 elements(),而只有 iterator() 了。那么如何将老版本的程序运行在新的 Java 编译器上呢? 如果不加修改,是肯定不行的,但是修改要遵循“开-闭”原则。我们可以用 Java 设计模式中的适配器模式解决这个问题。清单 14 所示是解决方法代码。

清单 14. 采用适配器模式

import java.util.ArrayList;

import java.util.Enumeration;

import java.util.Iterator;

import java.util.List;

 

public class NewEnumeration implements Enumeration

{

 

 Iterator it;

 public NewEnumeration(Iterator it)

 {

 this.it=it;

 // TODO Auto-generated constructor stub

 }

 

 public boolean hasMoreElements()

 {

 // TODO Auto-generated method stub

 return it.hasNext();

 }

 

 public Object nextElement()

 {

 // TODO Auto-generated method stub

 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());

 }

 }

}

清单 14 所示的 NewEnumeration 是一个适配器类,通过它实现了从 Iterator 接口到 Enumeration 接口的适配,这样我们就可以使用老版本的代码来使用新的集合对象了。

Java I/O 库大量使用了适配器模式,例如 ByteArrayInputStream 是一个适配器类,它继承了 InputStream 的接口,并且封装了一个 byte 数组。换言之,它将一个 byte 数组的接口适配成 InputStream 流处理器的接口。

我们知道 Java 语言支持四种类型:Java 接口,Java 类,Java 数组,原始类型(即 int,float 等)。前三种是引用类型,类和数组的实例是对象,原始类型的值不是对象。也即,Java 语言的数组是像所有的其他对象一样的对象,而不管数组中所存储的元素类型是什么。这样一来的话,ByteArrayInputStream 就符合适配器模式的描述,是一个对象形式的适配器类。FileInputStream 是一个适配器类。在 FileInputStream 继承了 InputStrem 类型,同时持有一个对 FileDiscriptor 的引用。这是将一个 FileDiscriptor 对象适配成 InputStrem 类型的对象形式的适配器模式。查看 JDK1.4 的源代码我们可以看到清单 15 所示的 FileInputStream 类的源代码。

清单 15. FileInputStream 类

 Public class FileInputStream extends InputStream{

/* File Descriptor - handle to the open file */

private FileDescriptor fd;

public FileInputStream(FileDescriptor fdObj) {

SecurityManager security = System.getSecurityManager(); 

if (fdObj == null) {

throw new NullPointerException();

}

if (security != null) {

security.checkRead(fdObj);}fd = fdObj; 

public FileInputStream(File file) throws FileNotFoundException {

String name = file.getPath();

SecurityManager security = System.getSecurityManager();

if (security != null) {

security.checkRead(name);

}

fd = new FileDescriptor();

open(name);

}

//其它代码

}

同样地,在 OutputStream 类型中,所有的原始流处理器都是适配器类。ByteArrayOutputStream 继承了 OutputStream 类型,同时持有一个对 byte 数组的引用。它一个 byte 数组的接口适配成 OutputString 类型的接口,因此也是一个对象形式的适配器模式的应用。

FileOutputStream 继承了 OutputStream 类型,同时持有一个对 FileDiscriptor 对象的引用。这是一个将 FileDiscriptor 接口适配成 OutputStream 接口形式的对象型适配器模式。

 

 

 

 

适配器模式使用注意事项

充当适配器角色的类就是:实现已有接口的抽象类;

为什么要用抽象类?此类是不要被实例化的。而只充当适配器的角色,也就为其子类提供了一个共同的接口,但其子类又可以将精力只集中在其感兴趣的地方。

适配器模式中被适配的接口 Adaptee 和适配成为的接口 Target 是没有关联的,Adaptee 和 Target 中的方法既可以是相同的,也可以是不同的。

适配器在适配的时候,可以适配多个 Apaptee,也就是说实现某个新的 Target 的功能的时候,需要调用多个模块的功能,适配多个模块的功能才能满足新接口的要求。

适配器有一个潜在的问题,就是被适配的对象不再兼容 Adaptee 的接口,因为适配器只是实现了 Target 的接口。这导致并不是所有 Adaptee 对象可以被使用的地方都能是使用适配器,双向适配器解决了这个问题。

优点

适配器模式也是一种包装模式,它与装饰模式同样具有包装的功能,此外,对象适配器模式还具有委托的意思。总的来说,适配器模式属于补偿模式,专用来在系统后期扩展、修改时使用。

缺点

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

适配器模式应用场景

在软件开发中,也就是系统的数据和行为都正确,但接口不相符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。比如在需要对早期代码复用一些功能等应用上很有实际价值。适用场景大致包含三类:

1、已经存在的类的接口不符合我们的需求;

2、创建一个可以复用的类,使得该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作;

3、在不对每一个都进行子类化以匹配它们的接口的情况下,使用一些已经存在的子类。

 

 

类适配器和对象适配器的权衡

 

类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。

对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理  Adaptee的子类了。

对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。

对于类适配器,适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。

对于对象适配器,要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。

对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。

对于对象适配器,需要额外的引用来间接得到Adaptee。

建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。

 

适配器模式的优点

更好的复用性

系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

 

更好的扩展性

在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。

 

适配器模式的缺点

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

0
3
分享到:
评论

相关推荐

    C#实例解析适配器设计模式

    看到一个园子里的朋友写了一篇ASP.NET的适配器设计模式的文章。其中提到了把输入的电压转换成灯泡适合的电压,这样才能使灯泡正常工作。正巧,我也在学习设计模式,其中翻看了一下秦小波写的《设计模式与禅》这本书...

    java的23种设计模式

    1、工厂模式 2、建造模式 3、工厂方法模式 4、原始模型模式 5、单例模式 6、适配器(变压器)模式 7、桥梁模式

    浅析java常用的设计模式(doc-23页).doc

    浅析java常用的设计模式(doc 23页) 1、工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即 可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修 改。如:如何...

    电源技术中的如何设计更高效、更低EMI的准谐振适配器

     准谐振转换器采用不连续导电模式(DCM)工作时,VDS必须从输入电压(Vin)与反射电压(Vreflect)之和降低到Vin。变压器初级电感(Lp)与节点电容(Clump,即环绕MOSFET漏极节点的所有电容组合值,包括MOSFET电容和变压器...

    电子测量中的安森美推出高能效紧凑型笔记本电脑适配器方案

     这些IC采用了专有的软跳周期(Soft Skip)模式,在跳周期模式期间渐进增加峰值电流 ,降低可听噪声的风险,从而节省更多的元器件,并简化变压器的设计和制造。这些新控制器结合跳周期和频率反走功能,在轻

    安森美推出高能效紧凑型笔记本电脑适配器方案

     这些IC采用了专有的软跳周期(Soft Skip)模式,在跳周期模式期间渐进增加峰值电流 ,降低可听噪声的风险,从而节省更多的元器件,并简化变压器的设计和制造。这些新控制器结合跳周期和频率反走功能,在轻

    TI - 为100W USB电力输送适配器提供超高功率密度

    USB 电力输送(PD)标准支持通过USB进行高达100W的电力输送,该电力足以...当设计人员想要实现高功率密度和小适配器尺寸时,上述不足就给功能的实现带来了很大的限制。  有源钳位反激(ACF)是一种可克服这些限制的新

    电源技术中的OnSemi NPC1337 60W适配器参考设计方案

    OnSemi公司的NCP1337是一款结合电流模式调节器和退磁检测器的芯片,以充分确保边界线/临界传导模式在任何负载/传输线条件连同最低漏电压开关(准-谐振操作)。因为Soxyless概念,变压器铁心重置检测在内部完成,没有...

    元器件应用中的产生双极性输出的无变压器 DC/DC 变换器

    虽然这种设计比较简单,但变压器本身会带来体积问题。把一个变压器装入一台要求减小电路占用面积和高度的设备中,这是具有挑战性的。图 1 所示电路可以从由3V ~10V 输入产生 ±5V 输出,适用于没有地方安装变压器的...

    电源技术中的基于SE3910的高性能隔离式AC/DC电源解决方案

    变压器次级反馈工作模式都需要低压端的恒压-恒流控制芯片协助完成电压的转换和实现恒流,此类应用方案增加了系统应用复杂程度,同时还增加系统方案的设计成本,本文要介绍的AC/DC电源控制芯片是思旺电子的SE3910,...

    电源技术中的利用高性能PWM控制器芯片SE3910构建AC/DC转换器解决方案

    变压器次级反馈工作模式都需要低压端的恒压-恒流控制芯片协助完成电压的转换和实现恒流,此类应用方案增加了系统应用复杂程度,同时还增加系统方案的设计成本,本文要介绍的AC/DC电源控制芯片是思旺电子的SE3910,...

    电源技术中的安森美推出固定频率电流模式控制器集成电路(IC)

     这些IC采用了专有的软跳周期(Soft Skip)模式,在跳周期模式期间渐进增加峰值电流 ,降低可听噪声的风险,从而节省更多的元器件,并简化变压器的设计和制造。这些新控制器结合跳周期和频率反走功能,在轻

    基于STM L4984D及SRK200A的400W适用于AIO电脑电源或大功率电源适配器方案-电路方案

    应用架构基于两个阶段:基于CCM(连续导通模式)升压PFC控制器的前端PFC预调节器,使用L4984D,以及围绕L6699器件设计的下游LLC谐振半桥转换器,提供12 V稳压输出电压,专用于为满足最严格的效率和待机规定的ATX或...

    java面试800题

    Q0051 设计模式主要几种 "创建模式 factory工厂模式、prototype原始模型模式、singleton单例模式、builder建造模式 结构模式 facade门面模式、proxy代理模式、adapter适配器(变压器)模式、composite合成模式、...

    实例解读51单片机完全学习与应用

    3.3 ■元器件插曲之一:变压器 3.3.1 变压器基础知识 3.3.2 电源变压器 3.4 ■元器件插曲之二:二极管与整流 3.4.1 二极管基础知识 3.4.2 整流 3.4.3 整流全桥 3.5 ■元器件插曲之三:电容 3.5.1 电容基础知识 3.5.2...

    电源技术中的PI TOPSwitcha-JX电源转换IC空载功耗达到85mW

    在低输入功率水平下,高效率还可使适配器的空载功耗降至最低,增大待机模式下对系统的供电量,这一点特别适用于受到能效标准和规范约束的产品应用。  由于采用全新的多周期调制模式,使得电源在空载条件下具有出色...

    双开关正激转换器及其应用设计

    安森美半导体的NCP1252是一款增强型双开关正激转换器,具有可调节开关频率及跳周期模式,带闩锁过流保护等多种保护特性,适合计算机ATX电源、交流适配器、UC38xx替代及其它任何需要低待机能耗的应用。

    【开源】样机CR6349+CR3006反激功率同步整流开关5V3.1A(工程文件+测试报告+BOM)-电路方案

    CR3006 是SOP-8 封装的一款结构简单同步整流开关,可工作于DCM和QR 模式,最高工作频率可达200KHZ,针对5V 输出电源系统进行了专门的优化和设计,内置RDS(ON) 为12mΩ的N 沟道MOS,用来替换传统的整流二极管,能...

Global site tag (gtag.js) - Google Analytics