Adapter模式介绍


现实中的“适配器”(Adapter的中文),可以让直流12伏特的笔记本在100伏特的AC电源下工作:

在程序中,经常会存在一些程序无法直接使用,需要进行适当转换才能使用,这这哦末弥补“现有程序”和“所需程序”之间差异的设计模式就是 Adapter模式

Adapter模式也被称为 Wrapper模式,因此,Apapter模式也被称为“包装器”或“适配器”

示例程序(使用继承的适配器)


介绍:
你现在需要将一个100伏的电流转换为12伏的适配器

关系:

电源的比喻示例程序
实际情况交流100伏特Banner类(showWithParen、showWithAster)
变换装置适配器PrintBanner类
需求直流12伏特Print接口(printWeak、printStrong)

类图

Banner类

public class Banner {
    private String string;
    public Banner(String string) {
        this.string = string;
    }
    public void showWithParen() {
        System.out.println("(" + string + ")");
    }
    public void showWithAster() {
        System.out.println("*" + string + "*");
    }
}
  • 提供了两个方法,(xxx) 表示弱电, *xxx* 表示强电

Print接口

public interface Print {
    public abstract void printWeak();
    public abstract void printStrong();
}

PrintBanner类

public class PrintBanner extends Banner implements Print {  
    public PrintBanner(String string) {  
        super(string);  
    }  
  
    @Override  
    public void printWeak() {  
       showWithParen();  
    }  
  
    @Override  
    public void printStrong() {  
        showWithAster();  
    }  
}
  • PrintBanner 作为适配器,继承了Banner又实现了Print

Main类

public class Main {  
    public static void main(String[] args) {  
        Print p = new PrintBanner("Hello");  
        p.printWeak();  
        p.printStrong();  
    }  
}

示例程序(使用委托的示例程序)


“委托”,就是“交给他人”,此示例程序旨在将无法处理的内容交给其他方法处理

类图:

根据类图来看,委托的方式和继承的类图非常相似,Print 不再是接口而是一个类,并且直接继承,而 Banner 也不是作为PrintBanner 的父类,而是直接聚合了它

因此,只需要修改Print类和PrintBanner类

Print类

public abstract class Print {
	public abstract void printWeak();
	public abstract void printStrong();
}

PrintBanner类

public class PrintBanner extends Print {
	private Banner banner;
	public PrintBanner(String string) {
		this.banner = new Banner(string);
	}
	public void printWeak() {
		banner.showWithParen();
	}
	public void printStrong() {
		banner.showWithAster();
	}
}

Adapter模式中的登场角色


  • Target(对象)
    • 该角色负责定义所需的方法。在示例程序中,Print 扮演此角色。
  • Client(请求者)
    • 该角色负责使用 Target 角色所定义的方法进行具体处理。在示例程序中,Main 扮演。
  • Adaptee(被适配)
    • 该角色持有一个既定方法,方便 Apapter 调用。在示例程序中,由 Banner 扮演
  • Apapter(适配)
    • 主人公,使用 Adaptee 的方法满足 Target 的需求。在示例程序中,PrintBanner 扮演。

角色的类图:

继承下

委托下

拓展要点


什么时候使用Adapter模式

Adapter模式会对现有的类进行适配,因此一些工具类(如Logg4j)等,可以使用 Adapter模式,让现有的类扮演 Adapter,直接满足需要方法即可,这样可以大大减少开发时间,并且可以降低自定义开发的Bug率。

没有现成的代码

即时现有的类没有我需要的功能,也尽量不修改已测试好的代码,可以自己编写一个自定义的。

版本升级与兼容性

如软件总是会进行更新的,我们如果使用 Adapter 模式使新旧版本兼容,那么可以同时维护新旧版本的代码。

使用案例


你现在需要在 FileIO 接口的基础上使用适配器模式,作出一个 继承自 PropertiesFileProperties

FileIO

public interface FileIO {  
    public void readFromFile(String filename) throws IOException;  
    public void writeToFIle(String fileName) throws IOException;  
    public void setValue(String key, String value);  
    public String getValue(String key);  
}

file.txt

year=1999

Properties是JDK自带的 java.util.Properties

编写 FileProperties

public class FileProperties extends Properties implements FileIO {  
    @Override  
    public void readFromFile(String fileName) throws IOException {  
        load(new FileInputStream(fileName));  
    }  
  
    @Override  
    public void writeToFIle(String fileName) throws IOException {  
        store(new FileOutputStream(fileName), "written by FileProperties");  
    }  
  
    @Override  
    public void setValue(String key, String value) {  
        setProperty(key, value);  
    }  
  
    @Override  
    public String getValue(String key) {  
        return getProperty(key);  
    }  
}

Main

public class Main {  
    public static void main(String[] args) throws IOException {  
        FileIO f = new FileProperties();  
        f.readFromFile("file.txt");  
        f.setValue("year", String.valueOf(new DateTime().year()));  
        f.setValue("month", String.valueOf(new DateTime().month() + 1));  
        f.setValue("day", String.valueOf(new DateTime().dayOfMonth()));  
        f.writeToFIle("newFile.txt");  
    }  
}

newFile.txt需要手动创建一下,最后如下:

#written by FileProperties  
#Tue Jan 07 19:08:45 CST 2025  
day=7  
file=xxx.png  
month=1  
year=2025