基本概念
适配器模式
结构型设计模式,将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作。它通过中间层(适配器)实现不同接口的转换,类似于现实中的电源适配器(如Type-C
转USB
接口)
核心结构:
- 目标接口(
Target
):客户端期望的接口;
- 适配者(``Adaptee`):需要被转换的接口,现有接口与目标接口不兼容;
- 适配器(
Adapter
):连接目标接口和适配者的中间层,实现接口转换。
两种实现方式
- 对象适配器(推荐):通过组合(聚合)适配者实现接口转换,符合
OOP
组合复用原则;
- 类适配器:通过继承适配者实现接口转换(Java中因单继承限制较少使用),不必重新实现整个被适配者,需要时也可覆盖被适配者的行为。
外观模式
结构型设计模式,为子系统的一组接口提供了一个统一的接口。外观模式定义了一个更高级别的接口,使得子系统更容易使用。
核心结构
- 外观类(
Facade
):提供简单统一的接口,封装子系统的复杂交互。
- 子系统类(
Subsystem Classes
):实现系统的核心功能,彼此可能有复杂的交互。
- 客户端(
Client
):通过外观类访问子系统功能。
示例说明
场景一:设计一个音频播放器系统,需要支持播放MP3
格式(已有接口)和WAV
格式(新接口),但播放器仅支持MP3
接口。
- 定义目标接口(
Target
)
1 2 3 4 5 6
|
public interface MediaPlayer { void playMp3(String fileName); }
|
- 定义适配者(``Adaptee`)
1 2 3 4 5 6
|
public interface MediaPlayer { void playMp3(String fileName); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
public class WavPlayer implements AdvancedMediaPlayer { @Override public void playWav(String fileName) { System.out.println("播放WAV文件:" + fileName); }
@Override public void playMp4(String fileName) { } }
|
- 实现适配器(
Adapter
)对象适配器:将WavPlayer
适配到MediaPlayer
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class MediaAdapter implements MediaPlayer { private AdvancedMediaPlayer advancedPlayer;
public MediaAdapter(AdvancedMediaPlayer advancedPlayer) { this.advancedPlayer = advancedPlayer; }
@Override public void playMp3(String fileName) { System.out.println("MP3播放由原接口处理"); }
@Override public void playWav(String fileName) { advancedPlayer.playWav(fileName); } }
|
- 调用方使用, 播放器实现类(使用目标接口)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class AudioPlayer implements MediaPlayer { private MediaAdapter mediaAdapter;
@Override public void playMp3(String fileName) { System.out.println("播放MP3文件:" + fileName); }
@Override public void playWav(String fileName) { if (mediaAdapter == null) { mediaAdapter = new MediaAdapter(new WavPlayer()); } mediaAdapter.playWav(fileName); } }
|
- 测试验证:
1 2 3 4 5 6 7 8
|
public static void main(String[] args) { AudioPlayer player = new AudioPlayer(); player.playMp3("music.mp3"); player.playWav("sound.wav"); }
|
1 2 3 4 5
| 执行结果 播放MP3文件:music.mp3 播放WAV文件:sound.wav
|
客户端使用适配器的做法如下:
- 客户通过使用目标接口,调用适配器的方法,对适配器做出请求。
- 适配器使用被适配接口,把请求翻译成被适配者上的一个或多个调用。
- 客户收到调用结果,但根本不知道是适配在进行转译工作。
其类图如下:
- 对象适配器

- 类适配器

场景二:电脑开机过程涉及电源、CPU、内存、硬盘等多个组件的协同工作,使用外观模式可以将复杂流程封装为简单接口。
- 定义电脑组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
class Power { public void on() { System.out.println("电源已开启"); } public void off() { System.out.println("电源已关闭"); } }
class CPU { public void start() { System.out.println("CPU初始化中..."); } public void stop() { System.out.println("CPU停止运行"); } }
class Memory { public void load() { System.out.println("加载系统到内存..."); } public void release() { System.out.println("释放内存资源"); } }
class HardDisk { public void read() { System.out.println("从硬盘读取系统数据..."); } }
|
- 定义电脑外观类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
class ComputerFacade { private Power power; private CPU cpu; private Memory memory; private HardDisk hardDisk; public ComputerFacade() { power = new Power(); cpu = new CPU(); memory = new Memory(); hardDisk = new HardDisk(); } public void powerOn() { power.on(); cpu.start(); memory.load(); hardDisk.read(); System.out.println("电脑开机完成!"); } public void powerOff() { cpu.stop(); memory.release(); power.off(); System.out.println("电脑关机完成!"); } }
|
- 测试验证
1 2 3 4 5 6 7 8 9 10 11
|
public class FacadePatternDemo { public static void main(String[] args) { ComputerFacade computer = new ComputerFacade(); computer.powerOn(); System.out.println("========"); computer.powerOff(); } }
|
使用外观模式,我们创建一个类,这个类把属于某些子系统的一套复杂的类简化和统一。和许多模式不同,外观模式相当直接了当,没有难以理解的抽象,但这并不会让它的作用减弱;外观模式让我们避免客户端和子系统之间的紧耦合。
类图如下:

还涉及到一新的面相对象原则: 【最少知识原则|墨忒耳法则】,只和你的密友谈话。
如何实现只和你的密友谈话,该原则提供类一个指南:对于任何对象,从该对象的任何方法,只调用属于以下范围的方法:
- 对象自身
- 作为参数传递给方法的类
- 该方法创建或实例化的任何对象
- 对象的任何组件
1 2 3 4 5 6 7 8 9 10 11
|
public float getTemp() { Thermometer thermometer = station.getThermometer(); return thermometer.getTemperature(); }
public float getTemp(){ return station.getTemperature(); }
|
总结
适配器模式
优点
- 耦合性低:客户端与适配者解耦,无需修改原有代码;
- 复用性强:适配者可被多个适配器复用,拓展新接口成本低;
- 开闭原则:适配器封装变化,无需修改目标接口和适配者,可以新增适配器实现扩展;
缺点
- 多层封装开销:过多适配器可能增加系统复杂度;
- 接口转换限制:无法转换适配者未提供的方法(仅能适配现有接口),实现适配器的接口和需要支持的接口大小成正比,若过大是否可以考虑重写客户端的接口调用,或者直接封装一个类,在类中封装所有变化;
使用场景
- 系统兼容:新旧系统接口不兼容时,通过适配器过渡;
- 第三方库集成:集成外部库时,将其接口转换为系统兼容的格式;
- 接口统一:将多个不同接口统一为标准接口(如
Java
的Enumeration
转Iterator
);
在JDK中的应用
java.io.InputStreamReader
:将InputStream
(字节流)适配为Reader
(字符流);
java.util.Arrays.asList
():将数组适配为List
接口;
Servlet
中的适配器:HttpServletAdapter
将标准Servlet
适配为HttpServlet
接口;
外观模式
优点
- 简化接口:为复杂子系统提供统一入口,客户端无需了解内部细节;
- 降低耦合:客户端与子系统解耦,子系统内部变化不影响客户端;
- 层次分明:分离了高层接口和底层实现,符合“最少知识法则”;
- 保护子系统:外观类可以控制对底层组件的访问,避免客户端直接操作导致混乱;
缺点
- 复杂度增加:应用“最少知识原则”会导致需要编写更多的“包装者”类来处理对其他组件的调用,导致系统复杂度和开发成本的增加,运行时性能下降;
使用场景
- 复杂系统的简化接口:如软件框架为用户提供简化的
API
(如Spring
框架对底层技术的封装);
- 新旧系统兼容:为旧系统提供新的统一接口,便于新功能集成;
- 模块解耦:作为模块间的接口,降低模块间的依赖;
- 分布式系统:为分布式服务提供统一的调用入口;
区别
- 适配器模式:适配器模式转换接口格式,外观模式简化接口复杂度,外观和适配器都可以包装多个类,但外观的意图是简化,而适配器的意图是转化接口为不同的接口;
- 中介者模式:中介者模式处理组件间的交互,外观模式封装子系统为单一接口;
- 单例模式:外观类通常设计为单例,确保全局唯一入口;