Java设计模式 —— 适配器(Adapter)

# 标签: 读博客

Adapter Pattern / Wrapper Pattern

什么时候需要适配,需要包装?肯定是你给我的,现有的服务提供的不好用,我才需要给你进行一下包装再用,或者适配之后再用。

Android中,数据的list能直接放到view上面么?不能吧,所以搞了个adapter适配一下,变成了一个封装类,这个封装类是可以安在view上,或者说给view提供数据源的。

你笔记本电脑需要12V的直流电,但是你家里只有220V交流电或者110V交流电,所提供的,并不是所需的,不能直接用,是不是要适配一下,包装一下我才能拿来用啊。

这个思维放到代码上就是,你提供的A接口(类)压根儿不好使,没办法我去搞了个Aadapter类或者AWrapper类或者Ainterface 给你包一层,或者说适配一下,姑且叫做B,那么我要用A,就直接用A的适配B就好了。

好了,你可以去看作者的原文了。(到后面我来告诉你作者真心没把这么好的例子讲好,当然我说了,我的水平并不在作者之上,这只是多方学习后的质疑)

原文转载自:http://blog.csdn.net/guolin_blog/article/details/9400141



今天一大早,你的leader就匆匆忙忙跑过来找到你:“快,快,紧急任务!最近ChinaJoy马上就要开始了,老板要求提供一种直观的方式,可以查看到我们新上线的游戏中每个服的在线人数。”

你看了看日期,不是吧!这哪里是马上要开始了,分明是已经开始了!这怎么可能来得及呢?

“没关系的。”你的leader安慰你道:“功能其实很简单的,接口都已经提供好了,你只需要调用一下就行了。”

好吧,你勉为其难地接受了,对于这种突如其来的新需求,你早已习惯。

你的leader向你具体描述了一下需求,你们的游戏目前有三个服,一服已经开放一段时间了,二服和三服都是新开的服。设计的接口非常轻便,你只需要调用Utility.getOnlinePlayerCount(int),传入每个服对应的数值就可以获取到相应服在线玩家的数量了,如一服传入1,二服传入2,三服则传入3。如果你传入了一个不存在的服,则会返回-1。然后你只要将得到的数据拼装成XML就好,具体的显示功能由你的leader来完成。

好吧,听起来功能并不是很复杂,如果现在就开始动工好像还来得及,于是你马上敲起了代码。

首先定义一个用于统计在线人数的接口PlayerCount,代码如下:

public interface PlayerCount {  
  
    String getServerName();  
  
    int getPlayerCount();  
  
}

接着定义三个统计类实现了PlayerCount接口,分别对应了三个不同的服,如下所示:

public class ServerOne implements PlayerCount {  
  
    @Override  
    public String getServerName() {  
        return "一服";  
    }  
  
    @Override  
    public int getPlayerCount() {  
        return Utility.getOnlinePlayerCount(1);  
    }  
  
}
public class ServerTwo implements PlayerCount {  
  
    @Override  
    public String getServerName() {  
        return "二服";  
    }  
  
    @Override  
    public int getPlayerCount() {  
        return Utility.getOnlinePlayerCount(2);  
    }  
  
}
public class ServerThree implements PlayerCount {  
  
    @Override  
    public String getServerName() {  
        return "三服";  
    }  
  
    @Override  
    public int getPlayerCount() {  
        return Utility.getOnlinePlayerCount(3);  
    }  
  
}

然后定义一个XMLBuilder类,用于将各服的数据封装成XML格式,代码如下:

public class XMLBuilder {  
  
    public static String buildXML(PlayerCount player) {  
        StringBuilder builder = new StringBuilder();  
        builder.append("<root>");  
        builder.append("<server>").append(player.getServerName()).append("</server>");  
        builder.append("<player_count").append(player.getPlayerCount()).append("</player_count>");  
        builder.append("</root>");  
        return builder.toString();  
    }  
  
}

这样的话,所有代码就完工了,如果你想查看一服在线玩家数只需要调用:

XMLBuilder.buildXML(new ServerOne());

查看二服在线玩家数只需要调用:

XMLBuilder.buildXML(new ServerTwo());

查看三服在线玩家数只需要调用:

XMLBuilder.buildXML(new ServerThree());

咦?你发现查看一服在线玩家数的时候,返回值永远是-1,查看二服和三服都很正常。

你只好把你的leader叫了过来:“我感觉我写的代码没有问题,但是查询一服在线玩家数总是返回-1,为什么会这样呢?”

“哎呀!”你的leader猛然想起,“这是我的问题,前面没跟你解释清楚。由于我们的一服已经开放一段时间了,查询在线玩家数量的功能早就有了,使用的是ServerFirst这个类。当时写Utility.getOnlinePlayerCount()这个方法主要是为了针对新开的二服和三服,就没把一服的查询功能再重复做一遍。”

听到你的leader这么说,你顿时松了一口气:“那你修改一下Utility.getOnlinePlayerCount()就好了,应该没我什么事了吧?”

“晤。。。本来应该是这样的。。。可是,Utility和ServerFirst这两个类都已经被打到Jar包里了,没法修改啊。。。”你的leader有些为难。

“什么?这不是坑爹吗,难道要我把接口给改了?”你已经泪流满面了。

“这倒不用,这种情况下可以使用适配器模式,这个模式就是为了解决接口之间不兼容的问题而出现的。”

其实适配器模式的使用非常简单,核心思想就是只要能让两个互不兼容的接口能正常对接就行了。上面的代码中,XMLBuilder中使用PlayerCount这个接口来拼装XML,而ServerFirst并没有实现PlayerCount这个接口,这个时候就需要一个适配器类来为XMLBuilder和ServerFirst之间搭起一座桥梁,毫无疑问,ServerOne就将充当适配器类的角色。修改ServerOne的代码,如下所示:

public class ServerOne implements PlayerCount {  
      
    private ServerFirst mServerFirst;  
      
    public ServerOne() {  
        mServerFirst = new ServerFirst();  
    }  
  
    @Override  
    public String getServerName() {  
        return "一服";  
    }  
  
    @Override  
    public int getPlayerCount() {  
        return mServerFirst.getOnlinePlayerCount();  
    }  
  
}

这样通过ServerOne的适配,XMLBuilder和ServerFirst之间就成功完成对接了!使用的时候我们甚至无需知道有ServerFirst这个类,只需要正常创建ServerOne的实例就行了。

需要值得注意的一点是,适配器模式不并是那种会让架构变得更合理的模式,更多的时候它只是充当救火队员的角色,帮助解决由于前期架构设计不合理导致的接口不匹配的问题。更好的做法是在设计的时候就尽量把以后可能出现的情况多考虑一些,在这个问题上不要向你的leader学习。

适配器:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。



我的补充

原文作者的文笔还是蛮好的。不像我,哈哈,垃圾到要哭。

不过作者讲问题思维顺序(或者重点)总是不对啊。你听我给你分析。

先说说正题:(先说作者好的地方)

作者最后一句,也正印证了adapter的实质:当前所提供不能满足客户(即调用方)所需,那么就要将现有服务进行包装或者进行适配后方可调用。

在上例中,作者是在适配器类中保留了原接口的实例,即(实质上是)调用被包裹类(此处即ServerFirst)的实体。

private ServerFirst mServerFirst;

(你就把ServerFirst和Utility看成功能类,功能接口,方便你理解,看完下面的分析你就懂了)

保留实例是实现adapter的一种方式------------按照原作者所说,是由于接口不兼容不能在一起工作的类可以在一起工作了(这句话虽然是对的,然而并不好理解),即Utility和XMLBuilder可以在一起工作(不理解就跳过这段,看下面的分析)。然而这也可能是另外一种说法,如果原有服务,比如说基础服务ok,只是想在原有基础上做扩展或者改造,也可以用这种适配模式,因为啥?因为我拿到了mServerFirst,在适配器类中如何使用,添加什么,是由我做主的,Client那边儿是直接找我适配器的,又不是找功能接口 (比如此例中的Utility) 啥的。看代码你就明白了

@Override  
    public int getPlayerCount() {  
        return mServerFirst.getOnlinePlayerCount() & Utility.getOnlinePlayerCount();  
    }

这就是上面所说的对基础服务升级的一种例子(方法体内我调用功能接口没有限制,可以升级,可以改造)

关于思维顺序和重点,你听我的理解。

那我就先问一句了,谁是上例中,谁是适配器类?

还看啥哟,ServerOne就是适配器类啊。

原文中,有这么一句:

XMLBuilder.buildXML(new ServerThree());

实际上是

XMLBuilder.buildXML(PlayerCount retuslt);

我们的目的就是得到1服在线人数。

并且霸道的client客户XMLBuilder,它就只认PlayCount这个接口,

其他的,怎么实现目的它不管。

ok,问题出现了,实际实现功能的接口,不管是原始的Utility还是后来升级的(改造成的)ServerFirst,和现有的接口PlayerCount压根不兼容啊。(现有提供的,不是所需的,当然马上就需要适配了)

怎么搞?

出现了,PlayerCount接口和Utility接口(ServerFirst)的适配类ServerOne,或者这样表述:把Utility接口给包装一下成ServerOne。

不能用Utility或者ServerFirst,还不让用ServerOne么?

我让ServerOne实现PlayerCount接口,你还不让用?呵呵,这样就解决问题了,我用的是PlayerCount接口(实际是其实现类ServerOne)并没有用(没有直接用)Utility,霸道的客户端也没整了。

这么一来,你看清楚了吧,其实问题的重点(讲适配的重点),不在Utility接口升级到ServerFirst接口,而在于 PlayerCount 和 Utility这样实际的功能接口之间的适配。

总结最后,我给你画个图吧:

原来是这么个关系:

走不通之后,后来变成了这么个关系:(画图工具不熟悉,先忍着吧)

(没事儿多想想直流电和交流电就好啦,不说了,今天练不了琴了,好困)

时间: 2024-10-16 18:57:25

Java设计模式 —— 适配器(Adapter)的相关文章

设计模式--适配器(Adapter)模式

今天学习另一个设计模式,适配器(Adapter)模式,这是一个共同方向,但有特殊要求,就应用到此设计模式.写到这里,想起很久以前,有写过一篇<ASP.NET的适配器设计模式(Adapter)>http://www.cnblogs.com/insus/archive/2013/02/04/2891426.html ,但是似乎没有适配器的味道. 比如一个系统,开发时设计好各种权限,但某一种,客户提出要求,需要一个特殊的权限来操作.只好开发一个适配器来让其有这个特殊操作权限. 用代码来举例吧. 先定

设计模式 适配器-Adapter

适配器模式:将一个类的接口,转换成客户期望的另一个接口.适配器让原本接口不兼容的类可以合作无间. 直接上图.下面是对象适配器的类图.由于Java不支持多继承.所以这是Java的适配器实现方式. 结合Head First例子:一个火鸡适配器 TurkeyAdapter实现Duck接口.里面有一个Turkey成员(Adaptee).

iOS常用设计模式——适配器Adapter

1.什么是适配器设计模式(Adapter)   (What) 适配器设计模式是一种结构型设计模式, 它的作用是把一个类的接口转换成客户希望的另外一个接口,从而使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 适配器设计模式有两种实现方式:1.)通过继承来实现两个接口,叫类适配器: 2.)通过引用来避免对象适配器继承被适配对象,叫对象适配器. 图1: 类适配器的UML图 在类适配器中,Adapter与Adaptee之间的关系是继承,我们通过继承来实现对Adaptee中+ specific

java演示适配器(adapter)模式

为什么要使用模式: 模式是一种做事的一种方法,也即实现某个目标的途径,或者技术. adapter模式的宗旨就是,保留现有类所提供的服务,向客户提供接口,以满足客户的需求. 类适配器:客户端定义了接口并实现了这个接口,这个接口中的方法在自己的类库中有更好的实现方式,但是方法名不一样,不能为用户所用,可以定义一个类实现这个接口,这个接口中的方法可以委托给自己类库中的方法实现效果,以满足客户的需求.这种方式叫做接口适配. // 已存在的.具有特殊功能.但不符合我们既有的标准接口的类 class Ada

Java 实现适配器(Adapter)模式

平时我们会常常碰到这种情况,有了两个现成的类,它们之间没有什么联系.可是我们如今既想用当中一个类的方法.同一时候也想用另外一个类的方法.有一个解决方法是.改动它们各自的接口.可是这是我们最不愿意看到的.这个时候Adapter模式就会派上用场了 适配器 模式 有三种方式,一种是对象适配器,一种是类适配器, 一种是接口适配器 下面举例说明: 类适配器 类图 public class DrawRectangle {//画方 public void drawRectangle(String msg) {

Java读源码学设计模式:适配器Adapter

适配器模式相关源码:slf4j-1.6.1.hibernate-3.6.7 大家都知道,log4j是一个广泛使用的日志工具,除此之外,sun公司在JDK中也有自己的日志工具,也就是java.util.logging.Logger.当然还有其他一些日志工具. 多种日志工具功能和使用方式类似,一般都包含debug.info.warn.error等日志级别的方法,但却没有实现共同的接口.这一点不像JDBC,虽然关系型数据库种类很多,例如MySQL.Oracle等,但是有一套统一的接口,也就是JDBC.

设计模式学习心得&lt;适配器 Adapter&gt;

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁.这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能. 这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能. 概述 意图 将一个类的接口转换成客户希望的另外一个接口. 适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 主要解决 主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的. 何时使用 系统需要使用现有的类,而此类

Java设计模式之适配器模式(Adapter Pattern)

Adapter Pattern的作用是在不改变功能的前提下转换接口.Adapter分为两类,一类是Object Adapter, 另一类是Class Adapter.由于Class Adapter的实现需要用到多继承,而Java不支持多继承,所以这里只关注Object Adapter. 在JDK1.5之前是没有 java.util.Iterator 接口的,java.util.Enumeration 接口起着 Iterator 的作用.那么如果我们需要维护一些年代比较久远的代码,可能就会面临着没

Java设计模式之适配器

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9400141 今天一大早,你的leader就匆匆忙忙跑过来找到你:“快,快,紧急任务!最近ChinaJoy马上就要开始了,老板要求提供一种直观的方式,可以查看到我们新上线的游戏中每个服的在线人数.” 你看了看日期,不是吧!这哪里是马上要开始了,分明是已经开始了!这怎么可能来得及呢? “没关系的.”你的leader安慰你道:“功能其实很简单的,接口都已经提供好了,你只需要调用一下就行