应用场景

使用者依赖的接口与提供者的接口不匹配时,就加一层适配,而不修改两端的代码

生活中使用的风扇,假设插头是两孔的,但是我们家里墙上的插座只有三孔的,这个时候就需要一个适配器了,这个适配器就是外面买的插座,以上场景为例,写一个例子:

三孔的插座:

public class ThreeOutlet {
/** 三孔插座 */
public void link(String positive, String negative, String earthWire) {
System.out.println(String.format("多一根地线,保证安全用电:%s %s %s", positive, negative, earthWire));
}
}

我们使用电风扇需要启动和关闭的接口:

public interface TwoOutlet {
/** 启动 */
void power(String positive, String negative); /** 关闭 */
void shutDown();
}

适配插座,外面买来的,可以使用两孔的、三孔的:

public class Adapter1 extends ThreeOutlet implements TwoOutlet {

    private String ew = "多一根地线";	// 私有内置地线

    @Override
public void power(String positive, String negative) {
link(positive, negative, ew);
} @Override
public void shutDown() {
System.out.println("拔掉插头");
} @Override
public void link(String positive, String negative, String earthWire) {
super.link(positive, negative, earthWire);
}
}

电风扇类:

public class Fanner {

    private TwoOutlet outlet;

    public void run() {
outlet.power("1", "0");
System.out.println("获得动力");
System.out.println("风扇运转");
outlet.shutDown();
System.out.println("关闭电力,风扇停止运转");
} public void setOutlet(TwoOutlet outlet) {
this.outlet = outlet;
}
}

测试类:

public class Test {

    public static void main(String[] args) {
Fanner fanner = new Fanner();
Adapter1 adapter1 = new Adapter1();
fanner.setOutlet(adapter1);
fanner.run();
}
}



类图:

适配器模式

定义

把一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作

意图

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

主要解决问题

要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的

何时使用

  1. 系统需要使用现有的类,而此类的接口不符合系统的需要
  2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口
  3. 通过接口转换,将一个类插入另一个类系中

优缺点

优点:

  1. 可以让任何两个没有关联的类一起运行
  2. 提高了类的复用
  3. 增加了类的透明度
  4. 灵活性好

缺点:

  1. 过多地使用适配器,会让系统非常零乱,不易整体进行把握
  2. 由于JAVA至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类

类图:



涉及到的角色:

  1. 目标(Target)角色:这就是所期待的接口,目标可以是具体的或抽象的类
  2. 源(Adaptee)角色:现有需要适配的接口
  3. 适配器(Adapter)角色:适配器类是本模式的核心;适配器将源接口转换为目标接口;显然,这一角色不能是接口,而必须是具体类

类的适配器模式的结构:

Target接口:

public interface Target {

    /** 源类也有的方法 */
void sampleOperation1(); /** 源类没有的方法 */
void sampleOperation2();
}

Adaptee源角色:

public class Adaptee {

    public void sampleOperation1(){
System.out.println("源类也有的方法");
}
}

Adapter适配器角色:

public class Adapter extends Adaptee implements Target {

    /** 源类和目标类都有的方法 */
@Override
public void sampleOperation1() {
super.sampleOperation1();
} /** 源类没有的方法,期望有这个方法 */
@Override
public void sampleOperation2() { }
}

对象的适配器模式的结构:

Target接口:

public interface Target {

    /** 源类也有的方法 */
void sampleOperation1(); /** 源类没有的方法 */
void sampleOperation2();
}

Adaptee源角色:

public class Adaptee {

    public void sampleOperation1(){
System.out.println("源类也有的方法");
}
}

Adapter适配器角色:

public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
} /** 源类和目标类都有的方法 */
@Override
public void sampleOperation1() {
this.adaptee.sampleOperation1();
} /** 源类没有的方法,期望有这个方法 */
@Override
public void sampleOperation2() {
System.out.println("适配一个方法");
}
}

需要注意的是,适配器不是在开始设计的时候用的,而是在解决现有的项目中的问题

指鹿为马VS指方为圆

指鹿为马

赵高指鹿为马的故事都应该听说过,下面以适配器模式做个小例子:

现在需要的是一匹马:

public interface NeedHorse {

    /** 需要一匹马 */
void needHorse();
}

但是有的是一只鹿:

public class DeerAdaptee {

    public void deer() {
System.out.println("我是一头鹿,有人要指鹿为马!");
}
}

把鹿适配为马,指鹿为马:

public class DeerChangeAdapter implements NeedHorse {

    private DeerAdaptee deerAdaptee;

    public DeerChangeAdapter(DeerAdaptee deerAdaptee) {
this.deerAdaptee = deerAdaptee;
this.deerAdaptee.deer();
} public void deer() {
System.out.println("我是一头鹿,有人要指鹿为马!");
} @Override
public void needHorse() {
System.out.println("我是一匹马(其实我的真实身份是一只鹿)");
}
}

测试类:

public class Test {

    public static void main(String[] args) {
System.out.println("这个是鹿还是马?");
DeerAdaptee adaptee = new DeerAdaptee();
DeerChangeAdapter adapter = new DeerChangeAdapter(adaptee);
adapter.needHorse();
}
}



类图:

指方为圆

古有指鹿为马,现有指方为圆,nice:

我们需要的是一个球:

public interface BallIF {

    /** 计算体积 */
Double calculateVolume(); /** 计算面积 */
Double calculateAcreage(); /** 半径取值 */
Double getRadius(); /** 半径赋值 */
Double setRadius(double radius);
}

现在有的是一个立方体:

public class Cube {

    private Double width;

    public Cube(Double width) {
this.width = width;
} /** 计算体积 */
public Double calculateVolume() {
return width * width * width;
} /** 计算面积 */
public Double calculateAcreage() {
return width * width;
} /** 长度取值 */
public Double getWidth() {
return width;
} /** 长度赋值 */
public void setWidth(Double width) {
this.width = width;
}
}

适配器适配一下,指方为圆:

public class MagicFinger implements BallIF {

    private double radius = 0;

    private static double PI = 3.14d;

    private Cube adaptee;

    public MagicFinger(Cube adaptee) {
super();
this.adaptee = adaptee;
radius = this.adaptee.getWidth();
} @Override
public Double calculateVolume() {
return (4.0d/3.0d) * PI * (radius * radius);
} @Override
public Double calculateAcreage() {
return PI * 4.0d * (radius * radius * radius);
} @Override
public Double getRadius() {
return radius;
} @Override
public Double setRadius(double radius) {
return this.radius = radius;
}
}

测试一下:

public class Test {
public static void main(String[] args) {
Cube cube = new Cube(2.0);
MagicFinger finger = new MagicFinger(cube);
System.out.println("半径:" + finger.getRadius());
System.out.println("体积:" + finger.calculateVolume());
System.out.println("面积:" + finger.calculateAcreage());
}
}



类图:



如果想有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式

最新文章

  1. C#序列化
  2. android面试题
  3. [转载]来,让我们谈一谈Normalize.css
  4. JDBC操作数据库,第一:jsp插入mysql数据库,坎坷摸索分享
  5. 操作系统与c语言
  6. 0512 Scrum 4.0
  7. Linux CentOS6.5下编译安装MySQL 5.6.16【给力详细教程】
  8. java web session监听销毁跳转
  9. MSSQLSERVER数据库- LEFT JOIN后面跟着WHERE竟变成内联 解决方法
  10. display:block; 块级元素。<a>,<span>标签设置宽度和高度
  11. Java利用POI生成Excel强制换行
  12. LINQ查询表达式基础
  13. angular路由操作
  14. JPA、Hibernate框架、通用mapper之间的关系及通用mapper的具体实现
  15. npm install出现"Unexpected end of JSON input while parsing near"
  16. db2 reorg详解
  17. cdqz2017-test10-rehearsal(CDQ分治&可持久化线段树&单调栈)
  18. 【刷题】BZOJ 3668 [Noi2014]起床困难综合症
  19. 容器启动报iptables错误
  20. jquery ui autocomplete 模拟百度搜索自动提示

热门文章

  1. Mysql训练:两个表中使用 Select 语句会导致产生 笛卡尔乘积 ,两个表的前后顺序决定查询之后的表顺序
  2. 如何删除Image元素下面的空白行及为什么行内元素有底线
  3. 关于GitHub 搭建 Hexo 总结
  4. Spring MVC 配置记录
  5. Android Studio|IntelliJ IDEA Git使用小技巧
  6. 上百本电子书(Java/Hadoop/Spark/Linux/机器学习/)免费分享 百度云持续更新
  7. Spring-05 使用注解开发
  8. 设计模式系列之享元模式(Flyweight Pattern)——实现对象的复用
  9. Educational Codeforces Round 98 (Rated for Div. 2)
  10. vs - 调试的技巧