天天看点

深入理解适配器模式(类适配器、对象适配器、接口适配器)适配器模式

适配器模式

文章目录

  • 适配器模式
    • 一、概述
    • 二、定义和结构
      • 1. 定义
      • 2. 结构
    • 三、类适配器模式
    • 四、对象适配器模式
    • 五、接口适配器模式
    • 六、适用场景

一、概述

如果去欧洲国家去旅游的话,他们的插座如下图最左边,是欧洲标准。而我们使用的插头如下图最右边所示,因此我们的笔记本电脑、手机在当地不能直接充电。所以就需要一个插座转换器,转换器第1面插入当地的插座,第2面供我们充电,这样使得我们的插头在当地能使用。这就是适配器模式的思想。

深入理解适配器模式(类适配器、对象适配器、接口适配器)适配器模式

二、定义和结构

1. 定义

  • 适配器模式是指将一个类的接口转换成另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作
  • 适配器模式分为类适配器模式和对象适配器模式
    • 类适配器模式的耦合度比较高,应用较少

2. 结构

适配器模式(Adapter)包含以下主要角色:

  • 目标接口:当前系统业务所期待的接口(想要转换为的接口),它可以是抽象类或接口,比如上图的最左边
  • 适配者类:它是被访问和适配的组件接口,比如上图的最右边
  • 适配器类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者,比如上图的中间位置

三、类适配器模式

实现方式:定义一个适配器类实现目标接口,同时又继承适配者类

例:

现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式,创建一个读卡器(适配器),将TF卡中的内容读取出来,对应的UML类图如下所示:

深入理解适配器模式(类适配器、对象适配器、接口适配器)适配器模式

代码实现:

  • SD卡接口(目标接口)
    public interface SDCard {
    
        //从SD卡中读取数据
        String readSD();
    }
               
  • SD卡实现类(目标类)
    public class SDCardImpl implements SDCard {
    
        @Override
        public String readSD() {
            String msg = "SDCard read msg : hello word SD";
            return msg;
        }
    }
               
  • Computer类
    public class Computer {
    
        //只能从SD卡中读取数据
        public String readSD(SDCard sdCard) {
            if(sdCard == null) {
                throw  new NullPointerException("sd card is not null");
            }
            return sdCard.readSD();
        }
    }
               
  • TF卡接口(适配者接口)
    public interface TFCard {
    
        //从TF卡中读取数据
        String readTF();
    }
               
  • TF卡实现类(适配者类)
    public class TFCardImpl implements TFCard {
    
        @Override
        public String readTF() {
            String msg = "TFCard read msg : hello word TFcard";
            return msg;
        }
    }
               
  • 适配器类(实现目标接口,继承适配者类)
    public class SDAdapterTF extends TFCardImpl implements SDCard {
    
        //要按照目标接口的方法访问,所以重写目标类的方法
        @Override
        public String readSD() {
            System.out.println("adapter read tf card");
            return readTF(); //访问适配者,通过继承拥有此方法
        }
    }
               
  • 进行测试
    public class Client {
        public static void main(String[] args) {
            //创建计算机对象
            Computer computer = new Computer();
            //读取SD卡中的数据
            String msg = computer.readSD(new SDCardImpl());
            System.out.println(msg);
            //SDCard read msg : hello word SD
    
            //使用该电脑读取TF卡中的数据
            //定义适配器类,通过适配器类按照目标接口的格式访问适配者
            String msg1 = computer.readSD(new SDAdapterTF());
            System.out.println(msg1);
            //adapter read tf card
            //TFCard read msg : hello word TFcard
        }
    }
               
  • 类适配器的缺点
    • 违背了合成复用原则
      • 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
    • 目标类有接口规范时才可以使用
      • 由于类的单继承性,适配器类无法继承两个类(只能一个接口一个类)

四、对象适配器模式

实现方式:定义一个适配器类实现目标接口,适配者作为适配器类的属性(使用组合或者聚合关系,类适配器模式采用的是继承关系)

继续对读卡器案例进行改进,对应的UML类图如下所示:

深入理解适配器模式(类适配器、对象适配器、接口适配器)适配器模式

代码实现:

  • 只需要修改适配器类的代码即可(实现目标接口)
    public class SDAdapterTF implements SDCard {
    
        //适配者作为属性出现,不再使用继承关系
        private TFCard tfCard;
    
        //构造器
        public SDAdapterTF(TFCard tfCard) {
            this.tfCard = tfCard;
        }
    
        //重写目标接口的方法,按照目标接口的格式访问
        @Override
        public String readSD() {
            System.out.println("adapter read tf card");
            return tfCard.readTF();
        }
    }
               
  • 进行测试
    public class Client {    
    	public static void main(String[] args) {        
    	//创建计算机对象       
    	Computer computer = new Computer();        
    	
    	//通过适配器类访问适配者,构造器中定义适配者        
    	SDAdapterTF sdAdapterTF = new SDAdapterTF(new TFCardImpl());        
    	String msg1 = computer.readSD(sdAdapterTF);        
    	System.out.println(msg1);        
    	//adapter read tf card        
    	//TFCard read msg : hello word TFcard    
    	}
    }
               
  • 优点
    • 遵循合成复用原则,解决了类适配器模式存在的问题

五、接口适配器模式

使用场景:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter ,实现此接口中的所有方法,并为该接口中的每个方法提供一个默认实现(空方法,使其不再成为抽象方法),那么该抽象类的子类可以有选择的覆盖抽象父类的某些方法来实现需求,而不必重写所有方法

六、适用场景

  • 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致
  • 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同