设计模式-适配器模式(结构型)

定义

将一个类的接口(被适配器的类)转换成客户期望的另一个接口(目标类),使原本接口不兼容的类可以一起工作。

主要分为两种

  • 对象适配器:通过组合实现
  • 类适配器:通过继承实现

适用场景

  • 已经存在的类,它的方法和需求不匹配时(方法结果相似或相同)
  • 不是软件设计阶段考虑的设计模式,而是软件维护过程中,由于不同产品,不同厂家造成功能类似而接口不同的情况下的解决方案。

优点

  • 提高类的透明性和复用性。
  • 目标类和适配器类解耦。
  • 符合开闭原则。

缺点

  • 增加系统复杂性
  • 增加代码阅读的难度

适配器模式模型

类适配器模式

类适配器模式,说通俗来说,就是适配器继承被适配类并且实现目标类所需要的接口。下面我们来简单的实现以下这个模式。

被适配的类

/**
 * 被适配的类
 */
public class AdaptedClass {

    public void AdaptedOwnMethod() {
        System.out.println("被适配者的方法");
    }
}

适配器目标类接口

/**
 * 适配器目标类接口
 */
public interface AdaptTarget {

    void request();
}

目标类的原始实现

/**
 * 目标类的原始实现
 */
public class TargetClass implements AdaptTarget {
    @Override
    public void request() {
        System.out.println("目标的原方法");
    }
}

适配器

/**
 * 适配器
 */
public class Adapter extends AdaptedClass implements AdaptTarget{

    public Adapter() {
        System.out.println("适配开始");
    }

    @Override
    public void request() {
        System.out.println("适配成功");
        System.out.println("目标类输出被适配器的方法结果--");
        super.AdaptedOwnMethod();
    }
}

UML

adaptor1.jpg

我们可以看到 适配器即继承了被适配的类,又实现了适配目标类的接口。因此我们可以通过他,来改变目标类原有逻辑。

在这个模型里,我们看到一个目标类的具体实现(TargetClass) ,当有业务改变时,该类可能已经不满足需求了,因此我们需要 适配器Adapter来改变目标类的实现。

适配测试类

public class Test {

    public static void main(String[] args) {
        //目标原方法
        AdaptTarget target = new TargetClass();
        target.request();

        System.out.println("-------业务调整 需要适配其他方法----------");

        //使用适配器,获取适配的目标类方法
        AdaptTarget adaptTarget = new Adapter();
        adaptTarget.request();

    }
}

结果

result.jpg

对象适配器

在这里 我们的被适配者(AdaptTarget)不变,适配器目标类接口(AdaptTarget)不变,目标原实现类(目标类的原始实现)不变。只需要改变适配器Adapter

public class Adapter implements AdaptTarget {
    //组合被适配的对象
    AdaptedClass adaptedClass = new AdaptedClass();

    public Adapter() {
        System.out.println("适配开始");
        System.out.println("组合 被适配对象 -- AdaptedClass");
    }

    @Override
    public void request() {
        System.out.println("适配成功");
        System.out.println("目标类输出被适配器的方法结果--");
        adaptedClass.AdaptedOwnMethod();
    }
}

我们可以看到,适配器不再继承被适配者的类,而是使用组合模式,将被适配者组合进了适配器中。

UML类图

adaptor2.jpg

结果

result.jpg

简单的业务示例

我们现在很多手机都取消了3.5mm手机孔,这让我们使用3.5mm 耳机的选手十分头疼,还好出了接口转换器,我们又可以使用3.5mm 耳机听音乐了。我们接下来实现一下这个转换的逻辑。

代码

Type-C 接口的手机

/**
 * MI9手机
 */
public class TypeCPhone {

    public void outputMusic() {
        System.out.println("Type-C 插孔 --  输出音乐源");
    }
}

在这里我们简单实现一个音乐输出的方法。

3.5mm 耳机孔接口

/**
 * 适配3.5mm耳机孔
 */
public interface ThreePointFive {
     void requireOutputMusic() ;
}

这里简单定义了 需要转换后输出音乐源。

转换器(适配器)

/**
 * 将Type-c 输出源转换为 3.5mm手机输出源
 */
public class Adapter implements ThreePointFive{

    private TypeCPhone typeCPhone = new TypeCPhone();

    public Adapter() {
        System.out.println("获取Type-C 输出源 ");
    }

    @Override
    public void requireOutputMusic() {
        typeCPhone.outputMusic();
        System.out.println("Type-C 输出源开始转换为3.5mm 输出源");

    }
}

在这里我们使用了对象适配的模式。

转换测试类

public class Test {
    public static void main(String[] args) {

        ThreePointFive threePointFive = new Adapter();
        threePointFive.requireOutputMusic();
    }
}

结果

result3.jpg

其他开源中的实现

我们来看一下 Spring 中 AOP中实现的一个 适配器模式。

package org.springframework.aop.framework.adapter;

import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import org.springframework.aop.Advisor;

public interface AdvisorAdapter {

   boolean supportsAdvice(Advice advice);

   MethodInterceptor getInterceptor(Advisor advisor);

}

我们可以看到 这是一个切面拦截适配接口,我们可以实现 方法执行前后的拦截适配作用,我们来看一下具体实现类。

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

   @Override
   public boolean supportsAdvice(Advice advice) {
      return (advice instanceof MethodBeforeAdvice);
   }

   @Override
   public MethodInterceptor getInterceptor(Advisor advisor) {
      MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
      return new MethodBeforeAdviceInterceptor(advice);
   }

}
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {

   @Override
   public boolean supportsAdvice(Advice advice) {
      return (advice instanceof AfterReturningAdvice);
   }

   @Override
   public MethodInterceptor getInterceptor(Advisor advisor) {
      AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
      return new AfterReturningAdviceInterceptor(advice);
   }

}

通过源码,我们可以很清楚的看到,适配器在AOP中拦截方法时的实现。

小结

适配器模式在工程维护中,可以起到高复用的效果。希望小伙伴们不要在开发中盲目的重复实现已有类似的逻辑。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容