设计模式:单例、工厂、适配器代理
在软件开发中,设计模式是经过验证的“最佳实践”,能够帮助开发者解决重复出现的问题,提升代码的可维护性、可扩展性和可读性。本文将聚焦四种常用设计模式——单例模式、工厂模式、适配器模式和代理模式,从核心原理到实战场景,带你全面掌握它们的用法与精髓。
一、单例模式
定义与核心价值
单例模式(Singleton Pattern)是一种创建型设计模式,它保证一个类在整个应用中只有一个实例,并提供一个全局访问点。这种模式的核心价值在于避免重复创建对象导致的资源浪费(如数据库连接、配置文件加载),同时确保全局状态的一致性。
核心解决问题
- 避免频繁创建销毁重量级对象(如线程池、缓存);
- 控制全局共享资源的访问(如日志器、配置管理器);
- 确保系统中某个类的实例唯一性(如全局计数器)。
实现方式与代码示例
单例模式的实现需注意线程安全和懒加载(按需创建),常见方式有以下几种:
- 饿汉式:类加载时直接创建实例,线程安全但可能提前占用资源。
public class HungrySingleton {
// 类加载时初始化,天然线程安全
private static final HungrySingleton INSTANCE = new HungrySingleton();
// 私有构造器阻止外部实例化
private HungrySingleton() {}
// 全局访问点
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
- 懒汉式(双重检查锁定):延迟创建,通过同步锁和volatile关键字保证线程安全。
public class LazySingleton {
// volatile防止指令重排序,确保实例创建完整
private static volatile LazySingleton INSTANCE;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (INSTANCE == null) { // 第一次检查:避免频繁加锁
synchronized (LazySingleton.class) {
if (INSTANCE == null) { // 第二次检查:防止多线程同时通过第一次检查
INSTANCE = new LazySingleton();
}
}
}
return INSTANCE;
}
}
应用场景
- 日志工具类:确保日志写入的顺序性;
- 配置管理类:全局共享一份配置信息;
- 数据库连接池:避免重复创建连接导致的性能损耗。
二、工厂模式
工厂模式(Factory Pattern)是创建型设计模式的典型代表,它将对象的创建逻辑从使用逻辑中分离,通过“工厂”统一管理对象创建,降低代码耦合度。根据复杂度不同,可分为简单工厂、工厂方法和抽象工厂三种形式。
1. 简单工厂模式
核心思想:由一个工厂类根据参数创建不同类型的产品实例,适合产品种类较少的场景。
代码示例:创建不同形状(圆形、矩形)的简单工厂
// 产品接口
public interface Shape {
void draw();
}
// 具体产品:圆形
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("绘制圆形");
}
}
// 具体产品:矩形
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("绘制矩形");
}
}
// 简单工厂
public class ShapeFactory {
// 根据类型创建产品
public static Shape createShape(String type) {
if ("circle".equals(type)) {
return new Circle();
} else if ("rectangle".equals(type)) {
return new Rectangle();
}
throw new IllegalArgumentException("未知形状类型");
}
}
// 使用场景
public class Client {
public static void main(String[] args) {
Shape circle = ShapeFactory.createShape("circle");
circle.draw(); // 输出:绘制圆形
}
}
缺点:新增产品需修改工厂类,违反“开闭原则”(对扩展开放,对修改关闭)。
2. 工厂方法模式
核心思想:为每个产品定义专属工厂,工厂接口负责规范创建行为,具体工厂实现创建逻辑,完美符合开闭原则。
代码示例:日志记录器(文件日志、数据库日志)的工厂方法
// 产品接口:日志记录器
public interface Logger {
void log(String message);
}
// 具体产品:文件日志
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("文件日志:" + message);
}
}
// 具体产品:数据库日志
public class DbLogger implements Logger {
@Override
public void log(String message) {
System.out.println("数据库日志:" + message);
}
}
// 工厂接口
public interface LoggerFactory {
Logger createLogger();
}
// 具体工厂:文件日志工厂
public class FileLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new FileLogger();
}
}
// 具体工厂:数据库日志工厂
public class DbLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new DbLogger();
}
}
// 使用场景
public class Client {
public static void main(String[] args) {
LoggerFactory factory = new FileLoggerFactory(); // 切换工厂即切换产品
Logger logger = factory.createLogger();
logger.log("系统启动成功");
}
}
3. 抽象工厂模式
核心思想:用于创建产品族(一组相关联的产品),每个具体工厂负责一个产品族的创建,解决多维度产品扩展问题。
应用场景:如不同操作系统(Windows、Mac)的UI组件(按钮、文本框),每个系统对应一个产品族,抽象工厂确保组件风格一致。
工厂模式总结
模式类型 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
简单工厂 | 产品少、变化少 | 实现简单 | 违反开闭原则 |
工厂方法 | 产品多、需频繁扩展 | 符合开闭原则 | 类数量增多 |
抽象工厂 | 多产品族、多维度扩展 | 统一产品族风格 | 扩展新产品困难 |
三、适配器模式
适配器模式(Adapter Pattern)是结构型设计模式,它将一个类的接口转换成客户端期望的另一个接口,使原本因接口不兼容而无法协作的类能够一起工作。
核心解决问题
- 整合遗留系统:老接口与新接口不匹配;
- 复用第三方组件:第三方库接口与本地系统不一致;
- 避免大规模修改现有代码:通过适配层隔离变化。
实现方式
- 类适配器:通过继承目标接口和适配者类实现(单继承语言限制较多);
- 对象适配器:通过组合适配者对象实现(更灵活,推荐使用)。
代码示例:播放器适配器
假设现有一个只能播放MP3的播放器,需适配能播放MP4的新接口:
// 目标接口:客户端期望的播放器接口
public interface AdvancedPlayer {
void playMp4(String filename);
}
// 适配者:已有的MP3播放器(接口不兼容)
public class Mp3Player {
public void playMp3(String filename) {
System.out.println("播放MP3:" + filename);
}
}
// 对象适配器:将MP3播放器适配为AdvancedPlayer
public class PlayerAdapter implements AdvancedPlayer {
private Mp3Player mp3Player; // 组合适配者对象
public PlayerAdapter(Mp3Player mp3Player) {
this.mp3Player = mp3Player;
}
@Override
public void playMp4(String filename) {
// 适配逻辑:将MP4播放请求转换为MP3播放(实际场景可能更复杂)
System.out.println("适配器转换MP4为MP3格式...");
mp3Player.playMp3(filename.replace(".mp4", ".mp3"));
}
}
// 使用场景
public class Client {
public static void main(String[] args) {
AdvancedPlayer player = new PlayerAdapter(new Mp3Player());
player.playMp4("music.mp4"); // 输出:适配器转换... 播放MP3:music.mp3
}
}
四、代理模式
代理模式(Proxy Pattern)是结构型设计模式,它为目标对象提供一个代理对象,并通过代理对象控制对目标对象的访问。代理模式可在不修改目标对象的前提下,增强其功能(如日志、权限校验、延迟加载)。
核心解决问题
- 远程访问控制:如RPC框架中通过代理调用远程服务;
- 资源优化:延迟加载大对象(如高清图片);
- 功能增强:在调用前后添加日志、事务、缓存等逻辑。
实现方式
- 静态代理:手动创建代理类,与目标类实现同一接口;
- 动态代理:运行时动态生成代理类(如Java的JDK动态代理、CGLIB)。
代码示例:图片加载的虚拟代理
// 目标接口:图片加载
public interface Image {
void display();
}
// 真实对象:高清图片(加载耗时)
public class HighResolutionImage implements Image {
private String filename;
public HighResolutionImage(String filename) {
this.filename = filename;
loadFromDisk(); // 模拟耗时加载
}
private void loadFromDisk() {
System.out.println("从磁盘加载高清图片:" + filename);
}
@Override
public void display() {
System.out.println("显示高清图片:" + filename);
}
}
// 静态代理:虚拟代理(延迟加载)
public class ImageProxy implements Image {
private HighResolutionImage realImage;
private String filename;
public ImageProxy(String filename) {
this.filename = filename;
}
@Override
public void display() {
// 延迟加载:需要时才创建真实对象
if (realImage == null) {
realImage = new HighResolutionImage(filename);
}
realImage.display();
}
}
// 使用场景
public class Client {
public static void main(String[] args) {
Image image = new ImageProxy("photo.jpg");
// 首次调用时加载图片
image.display();
// 再次调用直接显示(无需重复加载)
image.display();
}
}
总结:模式的选择与协同
单例、工厂、适配器、代理四种模式虽用途不同,但核心都是解耦:
- 单例模式解耦“全局唯一实例”的创建与使用;
- 工厂模式解耦“对象创建”与“业务逻辑”;
- 适配器模式解耦“新旧接口”的兼容性问题;
- 代理模式解耦“核心功能”与“增强逻辑”。
在实际开发中,模式常组合使用:例如,工厂模式创建的对象可通过代理模式添加日志,适配器模式适配的接口可由单例模式管理全局访问。选择模式时需避免过度设计,根据业务复杂度灵活取舍——简单场景用简单工厂,复杂扩展用工厂方法,接口不兼容用适配器,需增强控制用代理。