设计模式之适配器模式 adapter 适配器模式分类概念角色详解 类适配器 对象适配器 接口适配器 双向适配器

现实世界中的适配器模型

先来看下来几个图片,截图自淘宝

上图为港版的插头与港版的插座

上图为插座适配器卖家的描述图

上图为适配后的结果

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

这就是适配器模式在电源插座上的应用

我们看下在插座适配器中的几个重要角色

可以看得出来,大陆和港版插座面板,都是作为电源的角色,他们的功能是相似的或者说相近的

插头要使用插座,进而接通电流

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

港版插座面板

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

否则抛异常


设计模式是作为解决问题或者设计类层级结构时的一种思维的存在,而不是公式一样的存在!

原文地址:https://www.cnblogs.com/noteless/p/9596069.html

时间: 2024-10-17 03:20:21

设计模式之适配器模式 adapter 适配器模式分类概念角色详解 类适配器 对象适配器 接口适配器 双向适配器的相关文章

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

适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作. 1. 解决的问题 即Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作. 2. 模式中的角色 2.1 目标接口(Target):客户所期待的接口.目标可以是具体的或抽象的类,也可以是接口. 2.2 需要适配的类(Adaptee):需要适配的类或适配者类. 2.3 适配器(Adapter):通过包装一个需要适配的对象,把

如何让孩子爱上设计模式 —— 7.适配器模式(Adapter Pattern)

如何让孩子爱上设计模式 -- 7.适配器模式(Adapter Pattern) 概念相关 定义: 适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而 使原本因接口不匹配而无法在一起工作的两个类能够在一起工作. 简单点说: 两个彼此间没有太大关联的类,想进行交互完成某些事情,如果 直接去修改各自的接口,就显得有些繁琐了,可以加个中间类, 用它来协调两类之间的关系,完成相关业务.这种玩法就叫适配器模式! 两种适配器模式: 根据适配器类与适配者类的关系不同,适配器模式可分为 类适配器 和 对

设计模式之适配器模式(Adapter)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

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

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

设计模式 - 迭代器模式(iterator pattern) Java 迭代器(Iterator) 详解

迭代器模式(iterator pattern) Java 迭代器(Iterator) 详解 本文地址: http://blog.csdn.net/caroline_wendy 参考迭代器模式(iterator pattern): http://blog.csdn.net/caroline_wendy/article/details/35254643 Java的标准库(util)中包含迭代器接口(iterator interface), import java.util.Iterator; 继承(

设计模式 - 模板方法模式(template method pattern) 排序(sort) 详解

模板方法模式(template method pattern) 排序(sort) 详解 本文地址: http://blog.csdn.net/caroline_wendy 参考模板方法模式(template method pattern): http://blog.csdn.net/caroline_wendy/article/details/32159455 模板方法模式的一个主要的应用是排序(sort)算法. 对象的排列方式并不是完全相同, 所以需要排序(sort)算法compareTo()

设计模式 - 命令模式(command pattern) 宏命令(macro command) 详解

命令模式(command pattern) 宏命令(macro command) 详解 本文地址: http://blog.csdn.net/caroline_wendy 参考: 命名模式(撤销): http://blog.csdn.net/caroline_wendy/article/details/31419101 命令模式可以执行宏命令(macro command), 即多个命令的组合操作. 具体方法:  1. 其余代码与命令(撤销)一致 2. 添加宏命令(macro command),

JavaScript设计模式 Item9 --适配器模式Adapter

适配器模式(转换器面模式),一般是为要使用的接口,不符本应用或本系统使用,而需引入的中间适配层类或对象的情况.适配器模式的作用是解决两个软件实体间的接口不兼容的问题. 一.定义 适配器模式(Adapter)是将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),适配器模式使得原本由于接口不兼容而不能一起工作的那些类(对象)可以一些工作.速成包装器(wrapper). 适配器的别名是包装器(wrapper),这是一个相对简单的模式.在程序开发中有许多这样的场景:当我们试图

设计模式之适配器模式 Adapter

从现在开始,将转入设计模式中的结构型模式 定义与角色 工作场景 代码实现 /** * 被适配的类--相当于键盘 * @author bzhx * 2017年3月10日 */ public class Adaptee { public void request(){ System.out.println("可以完成客户请求的需要的功能"); } } Adaptee类 public interface Target { void handleReq(); } Target 接口 /** *