每日一个设计模式之【适配器模式】

文章目录

每日一个设计模式之【适配器模式】

☁️前言🎉🎉🎉

大家好✋,我是知识汲取者😄,今天给大家带来一篇有关适配器模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的 稳健性可移植性可维护性,同时还能让你的代码 更加精炼,具备 艺术美感
适配器相信大家在生活中一定有经常见到吧,手机的充电器的那个大头就是,它将220V电压转成了手机需要的电压,让手机能够适配220V电压,还有USB接口转接口也是,让不同的插头进行了适配。而在计算机世界中也存在适配器,比如在Java中适配器不是一个硬件,而是一个类,它能够让一个接口转成适配另一个类,从而通过这个接口调用被适配类中的方法,是不是感觉很神奇,现在就让我们开始学习吧(●ˇ∀ˇ●)

每日一个设计模式之【适配器模式】
推荐阅读
  • 设计模式导学:🚪传送门
  • 每日一个设计模式系列专栏:🚪传送门
  • 设计模式专属Gitee仓库:✈启程
  • 设计模式专属Github仓库:🚀上路
  • 知识汲取者的个人主页:💓点我哦

; 🌻适配器模式概述

  • 什么是适配器模式

    设配器模式(Adapter Pattern)属于结构型模式,它将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器模式(Wrapper Pattern)

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

    • 提高系统的兼容性,可以让任何两个没有关联的类一起运行
    • 提高了类的透明度1,能够面对抽象编程,满足了开闭原则,有利于系统的维护
    • 提高了类的复用,一个接口可以直接通过适配器调用另一个类的行为,不用再去重新创建一个新的类
    • 提高了程序的灵活性,一个类的接口想要转换成另一个接口,直接创建一个适配器就可以了,很灵活、方便 ……
  • 缺点

    • 提高了系统的复杂度,过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现(这也是透明度带来的缺点),一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构(要遵循中庸之道)
    • 具有一定的局限性,对于两个功能完全不同的AdapteeTarget的时,适配器模式是无法使用的 ……
  • 适配器模式的适用场景

  • 一个接口想要使用另一个类中的行为,可使用适配器模式
  • 想建立一个可以重复使用的类,用于一些彼此之间没有太大关联的一些类,可使用适配器模式 …… 生活中的应用:手机充电器、USB转接口 Spring中的应用:Spring中的通知就有用到适配器模式,通知(Advice)使用了适配器 AdviceAdapter,这是适配器接口,对于每一个通知都拥它们各自的适配器,比如前置通知(BeforeAdvice)使用的适配器是 BeforeAdviceAdapter,被适配的对象是拦截器,因为通知的本质对请求进行拦截,然后利用代理模式添加扩展代码,增强程序的功能(具体可以去参考Spring的源码) 适配器模式角色划分
  • 目标接口(Target):客户所需要的接口,是由适配器得到的最终产物,用于和客户进行交互,可以是抽象类、接口
  • 被适配者(Adaptee):被适配的目标,用于被适配器进行转换,是Target对象的实际运行者,一般会是一个具体的类
  • 适配器(Adapter):适配器模式的核心角色,它能够将Adaptee转成Target,是一个具体的类
  • 适配器模式的分类
  • 类适配器模式:适配器由一个类组成,通过继承Adaptee,然后实现Target的方法,最终完成适配器模式
  • 对象适配器模式:适配器由一个类组成,该类实现Target的方法,内置一个Adaptee对象,最终完成适配器模式
  • 接口适配器模式:适配器由一个抽象类组成,该抽象类同时实现Adapter接口和Target接口,最终完成适配器模式

🌱适配器模式的实现

🐳类适配器模式

  • 实现方式:让Adapter先继承Adaptee,然后实现Target,核心是使用继承
  • 解决的问题:将一个类的接口转换成客户希望的另外一个接口
  • 存在的问题:由于类适配器模式需要使用继承Adaptee,而在Java中只允许单一继承,这就导致Target必须是接口,限制了Target类型;同时继承也导致一个适配器只能去适配一个类,这样每次想要适配一个类就需要去创建一个适配器,这样很容易增加系统的复杂度。出现这个问题的本质就是该模式不符合面对对象基本设计原则==”多用组合,少用继承”==原则

示例:

问题描述:机器人拥有自己的行为,但是我们想要机器人能够拥有动物的行为

每日一个设计模式之【适配器模式】

创建Adaptee

创建Target

创建Adapter

测试

编写配置文件

编写配置文件读取类

  • Step1:创建Adaptee 1)Dog:
package com.hhxy.adaptee.imp;

import com.hhxy.adaptee.Animal;

public class Bird implements Animal {

    @Override
    public void cry() {
        System.out.println("鸟发出叽叽喳喳的叫声");
    }

    @Override
    public void run() {
        System.out.println("鸟在天空中飞");
    }
}

2)Bird:

package com.hhxy.adaptee.imp;

import com.hhxy.adaptee.Animal;

public class Dog implements Animal {

    @Override
    public void cry() {
        System.out.println("狗发出汪汪汪的叫声");
    }

    @Override
    public void run() {
        System.out.println("狗在地上跑");
    }
}

  • Step2:创建Target 1)RobotClass:
package com.hhxy.target;

public interface RobotClass {

    void cry();

    void run();
}

2)RobotClassImp:

package com.hhxy.target.imp;

import com.hhxy.target.RobotClass;

public class RobotClassImp implements RobotClass {

    @Override
    public void cry() {
        System.out.println("机器人叫");
    }

    @Override
    public void run() {
        System.out.println("机器人跑");
    }
}
  • Step3:创建Adapter 1)DogAdapter:
package com.hhxy.adapter.classAdapter.imp;

import com.hhxy.target.RobotClass;
import com.hhxy.adaptee.imp.Dog;

public class DogAdapter extends Dog implements Adapter,RobotClass {

    @Override
    public void cry() {
        System.out.print("机器人模仿");
        super.cry();
    }

    @Override
    public void run() {
        System.out.print("机器人模仿");
        super.run();
    }
}

2)BirdAdapter:

package com.hhxy.adapter.classAdapter.imp;

import com.hhxy.target.RobotClass;
import com.hhxy.adaptee.imp.Bird;

public class BirdAdapter extends Bird implements Adapter, RobotClass {

    @Override
    public void cry() {
        System.out.print("机器人模仿");
        super.cry();
    }

    @Override
    public void run() {
        System.out.print("机器人模仿");
        super.run();
    }
}
  • Step4:创建配置文件 class-adapter-config.xml:

    这个配置文件主要是用来得到Adapter对象的,适用配置文件获取Adapter对象能够完美符合开闭原则,实现代码解耦,同时很方便测试、维护


<config>
    <animal>com.hhxy.adapter.classAdapter.imp.DogAdapteranimal>
    <animal>com.hhxy.adapter.classAdapter.imp.BirdAdapteranimal>
    <animal>testanimal>
config>
  • Step5:编写配置文件读取类 ReadClassAdapterConfig:
package com.hhxy.read;

import com.hhxy.target.RobotClass;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.Constructor;

public class ReadClassAdapterConfig {

    public static RobotClass getRobot(){

        try{

            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

            Document document = documentBuilder.parse(ReadClassAdapterConfig.class.getResourceAsStream("/class-adapter-config.xml"));

            NodeList nodeList = document.getElementsByTagName("adapterName");

            Node classNode = nodeList.item(1).getFirstChild();

            String adapterName = classNode.getNodeValue().trim();

            Class cls = Class.forName(adapterName);

            Constructor constructor = cls.getDeclaredConstructor();

            constructor.setAccessible(true);

            RobotClass robot = (RobotClass) constructor.newInstance();

            return robot;
        } catch (Exception e) {

            e.printStackTrace();
            throw new RuntimeException("未找到该适配器,请检查配置文件或者添加一个适配器!");
        }

    }
}
  • Step6:测试
package com.hhxy.test;

import com.hhxy.read.ReadClassAdapterConfig;
import com.hhxy.target.RobotClass;
import com.hhxy.target.imp.RobotClassImp;

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

        RobotClass robot = new RobotClassImp();
        robot.cry();
        robot.run();
        System.out.println("-----------------------");

        RobotClass robotAnimal = ReadClassAdapterConfig.getRobot();
        robotAnimal.cry();
        robotAnimal.run();
    }
}

测试结果:

每日一个设计模式之【适配器模式】

可以查看以下执行方法时的时序图加深理解:

每日一个设计模式之【适配器模式】
从上面可以看到DogAdapter,就是Robot和Dog的转接口,它将Dog的行为给了Robot,让Robot能够具有Dog的行为,这和我们生活中的适配器是同样的效果,我们手机的充电器就是一个适配器,它能够将220V的电压转成我们手机所需要的电压,这里DogAdapter也是一样,将Dog的行为转成了Robot的行为,而它实现的基础是Java三大基本特性之一的多态

🐳对象适配器模式

  • 实现方式:让Adapter实现Target,然后再Adapter内部创建一个Adaptee对象,核心是使用聚合
  • 解决的问题:每次新增适配类,无需去创建适配器,提高了代码的复用;符合”多用组合,少用聚合”原则,降低了系统复杂度
  • 存在的问题:不够灵活,因为我们是使用Adapter实现Target接口的,这就导致Adapter必须实现Target的所有方法,但是有时候我们并不需要使用所有Target中的所有方法

示例:

问题描述:机器人拥有自己的行为,但是我们想要机器人能够拥有动物的行为

每日一个设计模式之【适配器模式】

创建Adaptee

创建Target

创建Adapter

编写配置文件

编写配置文件读取类

测试

  • Step1:创建Adaptee
    和类适配器中的代码一致,略……

  • Step2:创建Target 和类适配器的代码类似,略…

  • Step3:创建Adapter AnimalAdapter:

    之前需要使用BirdAdapter和DogAdapter两个适配器,而现在只需要一个适配器就能实现类适配器模式两个适配器的作用了,大大地提高了代码的复用

package com.hhxy.adapter.objectAdapter;

import com.hhxy.adaptee.Animal;
import com.hhxy.target.RobotObject;

public class AnimalAdapter implements RobotObject {

    private Animal animal;

    public AnimalAdapter(Animal animal) {
        this.animal = animal;
    }

    @Override
    public void cry() {
        System.out.print("机器人模仿");
        animal.cry();
    }

    @Override
    public void run() {
        System.out.print("机器人模仿");
        animal.run();
    }
}
  • Step4:编写配置文件 object-adapter-config.xml:

<config>

    <animalName>com.hhxy.adaptee.imp.BirdanimalName>
    <animalName>com.hhxy.adaptee.imp.DoganimalName>
    <animalName>testanimalName>

    <adapterName>com.hhxy.adapter.objectAdapter.AnimalAdapteradapterName>
    <adapterName>testadapterName>
config>
  • Step5:编写配置文件读取类
package com.hhxy.read;

import com.hhxy.adaptee.Animal;
import com.hhxy.target.RobotObject;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.Constructor;

public class ReadObjectAdapterConfig {
    public static RobotObject getRobot() {

        try {

            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

            Document document = documentBuilder.parse(ReadClassAdapterConfig.class.getResourceAsStream("/object-adapter-config.xml"));

            NodeList nodeListAnimal = document.getElementsByTagName("animalName");
            NodeList nodeListAdapter = document.getElementsByTagName("adapterName");

            Node classNodeAnimal = nodeListAnimal.item(0).getFirstChild();
            Node classNodeAdapter = nodeListAdapter.item(0).getFirstChild();

            String animalName = classNodeAnimal.getNodeValue().trim();
            String adapterName = classNodeAdapter.getNodeValue().trim();

            Class clsAnimal = Class.forName(animalName);
            Class clsAdapter = Class.forName(adapterName);

            Constructor constructorAnimal = clsAnimal.getDeclaredConstructor();
            Constructor constructorAdapter = clsAdapter.getDeclaredConstructor(Animal.class);

            constructorAnimal.setAccessible(true);
            constructorAdapter.setAccessible(true);

            Animal animal = (Animal) constructorAnimal.newInstance();
            RobotObject robot = (RobotObject) constructorAdapter.newInstance(animal);

            return robot;
        } catch (Exception e) {

            e.printStackTrace();
            throw new RuntimeException("未找到该适配器,请检查配置文件或者添加一个适配器!");
        }

    }
}
  • Step6:测试
package com.hhxy.test;

import com.hhxy.read.ReadObjectAdapterConfig;
import com.hhxy.target.RobotObject;
import com.hhxy.target.imp.RobotObjectImp;

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

        RobotObject robot = new RobotObjectImp();
        robot.cry();
        robot.run();
        System.out.println("-----------------------");
        RobotObject robotAnimal = ReadObjectAdapterConfig.getRobot();
        robotAnimal.cry();
        robotAnimal.run();
    }
}

测试结果:

每日一个设计模式之【适配器模式】

🐳接口适配器模式

  • 实现方式:将Adapter抽象成一个接口,然后使用用一个AbstractAdapter去实现它,再通过委托的方式进行实现,本质上还是属于对象适配器的一种特殊方式,只是比对象适配器模式多了一个抽象层
  • 解决的问题:十分灵活,适配器实现类不光可以任意实现Robot接口中的方法,而且还可以通过匿名内部类重写方法,非常方便

示例:

问题描述:我们想要机器人拥有Bird的cry行为,同时拥有Dog的run行为
思考:如果使用第一种方式需要同时创建两个机器人(RobtClass)对象,显然不符合题目描述;使用第二种方式,同样的也需要创建两个机器人(RobotObject)对象,但是可以通过改造Adapter来实现,我们可以通过在Adapter中直接创建一个Dog、Bird对象,然后通过两个调用来实现,这是一种实现方式。
PS:在这里显然是无法体现到接口适配器的厉害之处的,这是因为在Robot接口存在的方法太少了,在实际项目中一般Target接口中会存在大量的方法(实际项目中这种适配器模式是用的最多的!),而有时我们只想使用Adapter让Traget的部分方法与Adaptee进行适配,这时候如果来使用前面两种方式实现Adapter,就需要实现接口的所有方法,这是因为实现类都必须实现接口中的所有的方法,这就导致Adapter显得特别臃肿、混乱。
这个时候就可以使用接口适配器方式来实现了(●ˇ∀ˇ●)

每日一个设计模式之【适配器模式】

创建Adaptee

创建Target

创建Adapter

编写配置文件

编写配置文件读取类

测试

  • Step1:创建Adaptee 上同,略……

  • Step2:创建Target RobotInterface:

package com.hhxy.target;

public interface RobotInterface {

    void cry();

    void run();

    void a();

    void b();

    void c();

    void d();
}
  • Step3:创建Adapter 1)AbstractAdapter
package com.hhxy.adapter.interfaceAdapter;

import com.hhxy.target.RobotInterface;

public abstract class AbstractAdapter implements RobotInterface {

    @Override
    public void cry() {

    }

    @Override
    public void run() {

    }

    @Override
    public void a() {

    }

    @Override
    public void b() {

    }

    @Override
    public void c() {

    }

    @Override
    public void d() {

    }
}

2)AnimalAdapter

package com.hhxy.adapter.interfaceAdapter;

import com.hhxy.adaptee.Animal;
import com.hhxy.adaptee.imp.Bird;
import com.hhxy.adaptee.imp.Dog;

public class AnimalAdapter extends AbstractAdapter{

    private Animal dog = new Dog();

    private Animal bird = new Bird();

    @Override
    public void cry() {
        System.out.println("机器人模仿");
        bird.cry();
    }

    @Override
    public void run() {
        dog.run();
    }
}
  • Step4:编写配置文件 略……

  • Step5:编写配置文件读取类 略……

  • Step6:测试

package com.hhxy.test;

import com.hhxy.adapter.interfaceAdapter.AnimalAdapter;
import com.hhxy.target.RobotInterface;

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

        RobotInterface robot = new AnimalAdapter();
        robot.cry();
        robot.run();

    }
}

每日一个设计模式之【适配器模式】

🌲总结

  • 类适配器:类适配器采用 &#x7EE7;&#x627F;+&#x5B9E;&#x73B0;的方式完成适配器,它拥有适配器的一般性功能,但是不符合”多用组合,少用继承”原则,导致系统具有较高复杂度,同时也限制了Target的类别
  • 对象适配器:对象适配器采用 &#x7EC4;&#x5408;+&#x5B9E;&#x73B0;的方式完成适配器,它不仅拥有适配器的一般性功能,还满足了”多用组合,少用继承”原则,降低了系统复杂度,提高了程序的灵活性,但是由于它是直接实现Target接口,这就导致要适配器要实现Target的所有方法,包括一些无需适配的方法,这就会导致适配器很臃肿、混乱,所以面对一些大型系统就显得有点吃力
  • 接口适配器:接口适配器是在对象适配器的基础上新增一个抽象类,让抽象类去重写Target的所有方法,然后适配器只需要继承抽象类就行了 ,从而大大提高了适配器的灵活性,也让适配器显得更加简洁,十分适合大型系统

总的而言,三种适配器模式从上而下使用范围逐渐扩大,灵活性也逐渐扩大

自此,文章就结束了,如果觉得本文对你有一丢丢帮助的话😄,欢迎点赞👍+评论✍,您的支持将是我写出更加优秀文章的动力O(∩_∩)O

每日一个设计模式之【适配器模式】

上一篇:每日一个设计模式之【建造者模式】
下一篇:每日一个设计模式之【桥接模式】
参考文章

在次致谢

Original: https://blog.csdn.net/qq_66345100/article/details/127810443
Author: 知识汲取者
Title: 每日一个设计模式之【适配器模式】

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/656469/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球