现实世界中的适配器模型

 
先来看下来几个图片,截图自淘宝
上图为港版的插头与港版的插座
 
上图为插座适配器卖家的描述图
 
上图为适配后的结果

现实世界中适配器模式 角色分类

这就是适配器模式在电源插座上的应用
我们看下在插座适配器中的几个重要角色
 
可以看得出来,大陆和港版插座面板,都是作为电源的角色,他们的功能是相似的或者说相近的
插头要使用插座,进而接通电流

现实世界到代码的转换 电源插座代码示例

港版插座面板

package adapter;
/**目标角色 Target 接口
* 香港地区使用的插座面板,提供输出电流的功能
* @author noteless
*
*/
public interface TargetHongkongPanelInterface {
public void offerHongKongElectricity();
} package adapter;
/**目标角色 Target 某个具体的港版插座面板 实现类
* 香港地区使用的插座面板,提供输出电流的功能
* @author noteless
*
*/
public class TargetHongkongPanel implements TargetHongkongPanelInterface{
@Override
public void offerHongKongElectricity() {
System.out.println("港版面板 提供电流");
}
}

大陆地区插座面板

package adapter;
/**被适配角色 Adaptee 接口
* 大陆地区使用的插座面板,提供输出电流的功能
* @author noteless
*
*/
public interface AdapteeChinaMainlandPanelInterface {
public void offerChinaMainlandElectricity();
} package adapter;
/**被适配角色 Adaptee 某种具体类型的插座面板 实现类
* 大陆地区使用的插座面板,提供输出电流的功能
* @author noteless
*/ public class AdapteeChinaMainlandPanel implements AdapteeChinaMainlandPanelInterface{
@Override
public void offerChinaMainlandElectricity() {
System.out.println("国标面板 提供电流"); }
}

港版插头

package adapter;
/**客户角色 Client 港版插头
* @author noteless
*
*/
public class ClientHongKongSocket {
/**接受港版插座面板作为参数
* 港版插头,插入到港版插座面板
* @param targetHongkongPanel
*/
public void plugIn(TargetHongkongPanelInterface targetHongkongPanel) {
targetHongkongPanel.offerHongKongElectricity();
}
/*
* 测试主程序,港版插头 插入到适配器上
* 适配器插入到大陆面板上
*/
public static void main(String ...args) {
//港版插头
ClientHongKongSocket socket = new ClientHongKongSocket();
//大陆面板
AdapteeChinaMainlandPanel adapteeChinaMainlandPanel = new AdapteeChinaMainlandPanel();
//适配器
Adapter adapter = new Adapter(adapteeChinaMainlandPanel);
//港版插头 插到 适配器上
socket.plugIn(adapter);
}
}

插头适配器

package adapter;
/**适配器角色 Adapter
* 实现目标角色 TargetHongkongPanelInterface
* 组合使用被适配角色 AdapteeChinaMainlandPanelInterface
* 将对目标角色的方法调用转换为被适配角色的方法调用
* @author noteless
*
*/
public class Adapter implements TargetHongkongPanelInterface{
private AdapteeChinaMainlandPanelInterface adapteeChinaMainlandPanel; Adapter(AdapteeChinaMainlandPanel adapteeChinaMainlandPanel){
this.adapteeChinaMainlandPanel = adapteeChinaMainlandPanel;
} @Override
public void offerHongKongElectricity() {
adapteeChinaMainlandPanel.offerChinaMainlandElectricity();
}
}

执行港版插头的测试main方法

 

UML图

港版插头ClientHongKongSocket与港版插座面板 TargetHongKongPanelInterface接口关联
Adapter 实现了  港版插座面板 TargetHongKongPanelInterface接口
并且包含一个      大陆插座面板  AdapteeChinaMainlandPanelInterface 接口
适配器将对 港版插座面板的方法调用  转换为  大陆插座面板的方法调用
这就是整个适配器结构( 可以不关注实现类)
 
 
客户角色Client 要使用 目标角色Target
适配器模式就是要冒充目标角色Target,看起来有目标角色的行为
在OOP中,想要做到 就是实现或者继承或者拥有一个成员
总之:
适配器就是把被适配者转换为为目标

OOP中的适配器模式详解

意图:
将一个类的接口转换成客户希望的另外一个接口。
适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.
注意:此处说的接口,并不是单纯的指Interface,而是指一切可以提供方法调用的类型,可能是接口也可能是类
客户使用适配器的过程:
客户通过目标接口调用适配器的方法,对适配器发出请求
适配器使用被适配者接口把请求进行处理
客户接收到调用的结果,但是并未察觉这一切是适配器在起转换作用.
 

适配器分类

适配器三种模式
类适配器   
对象适配器
接口适配器
想要把一个类的接口转换为客户希望的另外一个接口
必须要有输入输出,有目标  有源
所以作为一个适配器,必须要 一手拿着被适配者也就是源  另一手拿着的是目标
想要转变为目标,那么必须得同目标时一样的类型,   在oop中想要成为目标类型 要么继承 要么实现
想要拥有被适配者,要么继承,要么实现,要么就是关联(拥有一个对象)
三种方式可以理解为按照  拥有被适配者 的方式进行划分的
如果继承Adaptee,那么就是类 适配器
如果拥有一个Adaptee,也就是拥有一个Adaptee对象,那么就是对象 适配器
如果实现Adaptee,那么就是 接口适配器
 
现在回想下,我们上面的例子
适配器 实现了目标接口,并且拥有一个Adaptee对象 作为属性,很显然就是对象适配器
 
类适配器
根据上面的描述,如果继承Adaptee,那么就是类 适配器,
在Java中不允许多继承,既然已经继承了Adaptee  ,那么就必须要求目标是一个接口(此处接口就是Interface)
这就有一定的局限性
而且,既然是继承被适配者类,那么,被适配者的子类拥有的方法和行为,他并不能拥有,也就是说不能适配被适配者的子类
优点,那就是,适配器作为被适配者的子类,自然拥有更多的操作空间,比如重写方法
 
 
对象适配器
如同我们上面的例子一样,如果把被适配者当做一个属性对象放到适配器中,这就是对象适配器
显然,他不要求目标一定是接口, 继承还是实现都可以
同类适配器比较的话,显然,他不能对被适配者 原来的一些方法进行操作,只能进行使用,不过也无伤大雅,不算缺点
因为他是拥有一个被适配者类型的对象,那么,被适配者和他的子类显然都可以作为具体的对象传入
 
 
接口适配器
按照我们的描述,如果实现了被适配者 Adaptee  那么就是接口适配器
具体说来:
不需要全部实现接口提供的方法
可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法)
那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
适用于一个接口不想使用其所有的方法的情况
 

接口适配器示例

接口

package interfaceadapter;
public interface Interfaces {
public void method1();
public void method2();
public void method3();
public void method4();
public void method5();
}

抽象类

package interfaceadapter;
/**
* @author noteless
*
*/
public abstract class AbstractClass implements Interfaces {
@Override
public void method1() { }
@Override
public void method2() { }
@Override
public void method3() { }
@Override
public void method4() { }
@Override
public void method5() { }
}

两个实现类

package interfaceadapter;
public class ImplementClass1 extends AbstractClass {
@Override
public void method1() {
System.out.println("method1 called ");
}
@Override
public void method3() {
System.out.println("method3 called ");
}
@Override
public void method5() {
System.out.println("method5 called ");
}
} package interfaceadapter;
public class ImplementClass2 extends AbstractClass {
@Override
public void method2() {
System.out.println("method2 called");
} @Override
public void method4() {
System.out.println("method4 called");
}
}

测试类-客户角色

package interfaceadapter;
public class Test {
public static void main(String[] args) {
Interfaces class1 = new ImplementClass1();
Interfaces class2 = new ImplementClass2();
class1.method1();
class1.method2();
class1.method3();
class1.method4();
class1.method5();
System.out.println("------");
class2.method1();
class2.method2();
class2.method3();
class2.method4();
class2.method5();
}
}
 
接口适配器的行为相当于适配了自己
把原来的接口 当做被适配者
目标则是一个实现了接口部分功能的类
调用这个接口的部分方法场景下,上面的形式是非常方便的
 
从这个示例中或许应该更加能理解适配器的本意:
将一个类的接口转换成客户希望的另外一个接口。
适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
所以说,但凡是涉及到转换这一概念,你都可以考虑这个思维模式
三种常用形式,只是概念的表现形式而已
而且,实际的问题场景将会有很多种,也不可能完全都按照某种格式
 
 
再比如双向适配器
即可以将被适配者转换为目标
也可以把目标转换为被适配者

双向适配器

目标接口/目标实现类

package doubleadapter;
public interface TargetInterface {
void targetRequest();
} package doubleadapter;
public class TargetImplClass implements TargetInterface{
@Override
public void targetRequest() {
System.out.println("targetRequest ... ");
}
}

被适配者接口/被适配者实现类

package doubleadapter;
public interface AdapteeInterface {
void adapteeRequest();
} package doubleadapter;
public class AdapteeImplClass implements AdapteeInterface{
@Override
public void adapteeRequest() {
System.out.println("adapteeRequest ... ");
}
}

适配器

package doubleadapter;
public class Adapter implements TargetInterface,AdapteeInterface {
private TargetInterface target;
private AdapteeInterface adaptee;
Adapter(TargetInterface target){
this.target = target;
} Adapter(AdapteeInterface adaptee){
this.adaptee = adaptee;
}
@Override
public void adapteeRequest() {
target.targetRequest();
}
@Override
public void targetRequest() {
adaptee.adapteeRequest();
}
}

Client 客户端角色

Main方法就相当于Client 客户端角色
 
适配器Adapter模式的宗旨是:
保留现有类所提供的服务,向客户提供接口,以满足客户的期望,也就是将现有接口转换为客户希望的另外的一个接口
本质在于转换
 

JDK中的小应用

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.commons.collections.iterators;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
public class EnumerationIterator implements Iterator {
private Collection collection;
private Enumeration enumeration;
private Object last; public EnumerationIterator() {
this((Enumeration)null, (Collection)null);
}
public EnumerationIterator(Enumeration enumeration) {
this(enumeration, (Collection)null);
} public EnumerationIterator(Enumeration enumeration, Collection collection) {
this.enumeration = enumeration;
this.collection = collection;
this.last = null;
} public boolean hasNext() {
return this.enumeration.hasMoreElements();
} public Object next() {
this.last = this.enumeration.nextElement();
return this.last;
} public void remove() {
if (this.collection != null) {
if (this.last != null) {
this.collection.remove(this.last);
} else {
throw new IllegalStateException("next() must have been called for remove() to function");
} } else {
throw new UnsupportedOperationException("No Collection associated with this Iterator");
}
} public Enumeration getEnumeration() {
return this.enumeration;
} public void setEnumeration(Enumeration enumeration) {
this.enumeration = enumeration;
}
}
Enumeration 和 Iterator 大家应该都听过
Enumeration算是遗留的老代码了
很显然,我们希望能够使用新世界的Iterator
怎么办呢?
答案就是适配器
目标是 Iterator   被适配者是 Enumeration
看代码可知:
public class EnumerationIterator implements Iterator {
    private Enumeration enumeration;
 
他实现了Iterator  并且有一个Enumeration 的成员,是  对象适配器
nextElement()  与  next()
hasMoreElements  与  hasNext()    
他们可以说是匹配的
但是Enumeration 是不能删除的,没办法搞remove方法
所以说源码中提供了可以传入集合的构造方法,把对应的集合也传入进去
并且设置last变量记住刚才的位置
如果传递了集合 并且last存在,那么可以执行remove
否则抛异常
 
 
设计模式是作为解决问题或者设计类层级结构时的一种思维的存在,而不是公式一样的存在!
 
 

适配器模式 adapter 结构型 设计模式(九)的更多相关文章

  1. 设计模式06: Adapter 适配器模式(结构型模式)

    Adapter 适配器模式(结构型模式) 适配(转换)的概念无处不在:电源转接头.电源适配器.水管转接头... 动机(Motivation)在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象 ...

  2. 桥接模式 桥梁模式 bridge 结构型 设计模式(十二)

      桥接模式Bridge   Bridge 意为桥梁,桥接模式的作用就像桥梁一样,用于把两件事物连接起来   意图 将抽象部分与他的实现部分进行分离,使得他们都可以独立的发展.  意图解析 依赖倒置原 ...

  3. 享元模式 FlyWeight 结构型 设计模式(十五)

    享元模式(FlyWeight)  “享”取“共享”之意,“元”取“单元”之意. 意图 运用共享技术,有效的支持大量细粒度的对象. 意图解析 面向对象的程序设计中,一切皆是对象,这也就意味着系统的运行将 ...

  4. 代理模式 PROXY Surrogate 结构型 设计模式(十四)

    代理模式 PROXY 别名Surrogate 意图 为其他的对象提供一种代理以控制对这个对象的访问. 代理模式含义比较清晰,就是中间人,中介公司,经纪人... 在计算机程序中,代理就表示一个客户端不想 ...

  5. 组合模式 合成模式 COMPOSITE 结构型 设计模式(十一)

    组合模式(合成模式 COMPOSITE) 意图 将对象组合成树形结构以表示“部分-整体”的层次结构. Composite使得用户对单个对象和组合对象的使用具有一致性.   树形结构介绍 为了便于理解, ...

  6. 享元模式/Flyweight模式/对象结构型/设计模式

    flyweight 享元模式(对象结构型) Flyweight在拳击比赛中指最轻量级,即"蝇量级"或"雨量级",这里选择使用"享元模式"的意 ...

  7. 12、Decorator 装饰器 模式 装饰起来美美哒 结构型设计模式

    1.Decorator模式 装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式(Decorator Pattern)允许向一个现 ...

  8. 11、Composite 组合模式 容器与内容的一致性(抽象化) 结构型设计模式

    1.Composite模式定义 组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象.组合模式依据树形结构来组合对象,用来表示部分以及整体层次.这 ...

  9. 结构型设计模式之适配器模式(Adapter)

    结构 意图 将一个类的接口转换成客户希望的另外一个接口.A d a p t e r 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 适用性 你想使用一个已经存在的类,而它的接口不符合你 ...

随机推荐

  1. Express安装

    安装Express 安装好node.js的前提下,再来安装Express. 1.按win+rR,打开“运行”对话框,输入:“cmd”. 2.需要创建一个目录,然后进入目录并作为当前工作目录. mkdi ...

  2. js将一个具有相同键值对的一维数组转换成二维数组

    这两天,一个前端朋友在面试的笔试过程中遇到了一道类似于"用js实现将一个具有相同code值的一维数组转换成相同code值在一起的二维数组"的题目.他面试过后,把这个问题抛给了我,问 ...

  3. h5唤起APP并检查是否成功

    // 检查app是否打开 function checkOpen(cb) { const clickTime = +(new Date()); function check(elsTime) { if ...

  4. Java_接口与抽象类

    接口: 接口,英文interface,在java中,泛指供别人调用的方法或函数.接口是对行为的一种抽象. 语法: [public] interface InterfaceName{} 注意: 1)接口 ...

  5. dedecms 后台 菜单点击后打开的慢

    原因之一: 加载后台信息的时候  耗费的时间太长. 如果不关注这边的数据, 可以将他们删除掉. 删除  “信息统计”   : 找到   www.yoursite.com/dede/js/indexbo ...

  6. VUE 出现Access to XMLHttpRequest at 'http://192.168.88.228/login/Login?phone=19939306484&password=111' from origin 'http://localhost:8080' has been blocked by CORS policy: The value of the 'Access-Contr

    报错如上图!!!!    解决办法首先打开 config -> index.js ,粘贴 如下图代码,'https://www.baidu.com'换成要访问的的api域名,注意只要域名就够了, ...

  7. mybatis逆向工程的注意事项,以及数据库表

    1.选择性更新,如果有新参数就更换成新参数,如果参数是null就不更新,还是原来的参数 2.mybatis使用逆向工程,数据库建表的字段user_id必须用下滑线隔开,这样生成的对象private L ...

  8. Java构造方法、成员变量初始化以及静态成员变量初始化三者的先后顺序是什么样的?

    [Java笔试真题]:构造方法.成员变量初始化以及静态成员变量初始化三者的先后顺序是什么样的? [解答]:当类第一次被加载的时候,静态变量会首先初始化,接着编译器会把实例变量初始化为默认值,然后执行构 ...

  9. Vue(day2)

    一.过滤器 Vue中可以自定义过滤文本插值的过滤器.目前有两个地方可以使用: 插值文本{{ var }}中使用. 在v-bind中使用.( 2.1.0+ ) 用法:使用管道连接符 | 将需要过滤的文本 ...

  10. [Swift]LeetCode979. 在二叉树中分配硬币 | Distribute Coins in Binary Tree

    Given the root of a binary tree with N nodes, each node in the tree has node.val coins, and there ar ...