目录
设计模式是软件开发中解决常见问题的可复用方案。本文将深入探讨四种常用的设计模式:单例模式、工厂模式、适配器模式和代理模式,分析它们的原理、使用场景、优缺点,并提供实现代码示例。
一、单例模式(Singleton Pattern)
原理
单例模式确保一个类只有一个实例,并提供一个全局访问点。它通过私有化构造函数、提供静态获取实例方法来实现。
使用场景
需要控制资源访问,如数据库连接池
全局配置对象
日志记录器
设备驱动程序
优缺点
优点:
严格控制实例数量
全局访问方便
避免频繁创建销毁对象
缺点:
违反单一职责原则(既管理生命周期又处理业务)
难以扩展
多线程环境下需要特殊处理
实现代码
基础实现(非线程安全)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全实现(加锁)
public class ThreadSafeSingleton {
// 必须使用 volatile 禁止指令重排序,保证可见性
private static volatile ThreadSafeSingleton instance;
// 私有构造方法,防止外部通过 new 创建实例
private ThreadSafeSingleton() {}
public static ThreadSafeSingleton getInstance() {
// 第一次检查:避免每次都进入同步块,提高性能
if (instance == null) {
synchronized (ThreadSafeSingleton.class) {
// 第二次检查:确保只有一个线程能创建实例
if (instance == null) {
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}
}
/**
* 线程安全的单例模式(双重检查锁定 DCL,Double-Checked Locking)
*
* 为什么它是线程安全的?
*
* 1. 双重检查(Double-Check)
* 第一次检查 instance == null 时未加锁,可以避免每次调用 getInstance() 都进入同步块,
* 只有真正需要第一次初始化时才进入 synchronized,降低锁粒度,提高性能。
*
* 2. synchronized 块
* 第二次检查 instance == null 时已经在同步块内部,保证同一时刻只有一个线程能执行
* `instance = new ThreadSafeSingleton();`,防止重复创建对象。
*
* 3. volatile 关键字
* 修饰 instance 的 `volatile` 有两个关键作用:
*
* a) 可见性:保证一个线程修改 instance 后,其他线程立即可见最新值。
*
* b) 禁止指令重排序:防止 JVM/CPU 的指令重排序导致对象尚未完全构造完成就被其他线程看到。
* 创建对象实际上分为三步:
* 1. 分配内存
* 2. 初始化对象(调用构造方法)
* 3. 将引用指向刚分配的内存地址
* 如果没有 volatile,2 和 3 可能被重排序,导致其他线程拿到一个“半初始化”的对象。
*
* 所以:通过双重检查 + synchronized 保证互斥性,通过 volatile 保证可见性和禁止重排序,
* 从而确保在任何并发场景下都只有一个实例被创建,并且所有线程都能正确看到完全初始化的实例。
*/
第一次检查:避免已经创建好的实例每次调用都进锁,提升性能。
第二次检查:真正要创建时才进锁,防止多线程同时第一次创建,保证只 new 一次。
Java枚举实现(最佳实践)
public enum EnumSingleton {
// 唯一实例
INSTANCE;
// 可以有任何字段或方法
private int counter = 0;
public void doSomething() {
counter++;
System.out.println("counter = " + counter);
}
}
/**
* 天生线程安全
* 枚举常量在类加载阶段由 JVM 保证只实例化一次,且是原子操作;
* 不需要任何同步代码,也永远不存在并发问题。
*
*/
二、工厂模式(Factory Pattern)
原理
工厂模式定义一个创建对象的接口,但让子类决定实例化哪个类。它将对象创建与使用分离。
使用场景
系统需要独立于其产品的创建、组合和表示
需要提供产品的类库,只暴露接口
需要灵活扩展系统时
优缺点
优点:
解耦对象创建与使用
易于扩展新产品
符合开闭原则
缺点:
增加了系统复杂度
需要引入额外类
实现代码
简单工厂
// ====== 简单工厂 ======
// 简单工厂:把“创建对象”的逻辑集中在一个类里,根据传入的 type 返回不同的产品实现
public class SimpleFactory {
// 根据字符串 type 创建对应的产品对象
public Product createProduct(String type) {
switch (type) {
case "A":
return new ConcreteProductA();
case "B":
return new ConcreteProductB();
default:
throw new IllegalArgumentException("Unknown product type");
}
}
}
// 产品接口
interface Product {}
// 具体产品 A
class ConcreteProductA implements Product {}
// 具体产品 B
class ConcreteProductB implements Product {}
工厂方法
// ====== 工厂方法 ======
// 工厂方法:把“创建对象”推迟到子类实现,父类只定义流程骨架
public abstract class Factory {
// 子类必须实现,决定真正创建哪种产品
public abstract Product createProduct();
// 模板方法:先创建产品再使用
public void doSomething() {
Product product = createProduct();
// 使用product
}
}
// 具体工厂 A:负责生产 ConcreteProductA
class ConcreteFactoryA extends Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂 B:负责生产 ConcreteProductB
class ConcreteFactoryB extends Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
抽象工厂
// ====== 抽象工厂 ======
// 抽象工厂:一次性创建“一族”相关或依赖的产品(这里为 ProductA + ProductB)
public interface AbstractFactory {
// 创建 ProductA 系列对象
ProductA createProductA();
// 创建 ProductB 系列对象
ProductB createProductB();
}
// 具体工厂 1:生产 ProductA1 与 ProductB1
class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ProductA1();
}
@Override
public ProductB createProductB() {
return new ProductB1();
}
}
// 具体工厂 2:生产 ProductA2 与 ProductB2
class ConcreteFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ProductA2();
}
@Override
public ProductB createProductB() {
return new ProductB2();
}
}
三、适配器模式(Adapter Pattern)
原理
适配器模式将一个类的接口转换成客户期望的另一个接口,使原本不兼容的类可以一起工作。
使用场景
需要使用现有类,但其接口不符合要求
需要创建可复用的类,与不相关或不可预见的类协同工作
需要使用几个子类,但子类化每个类不现实
优缺点
优点:
使不兼容的接口协同工作
提高类的复用性
灵活、可扩展
缺点:
过多使用会使系统混乱
增加系统复杂度
实现代码
类适配器(继承)
// 目标接口
interface Target {
void request();
}
// 需要适配的类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee's specific request");
}
}
// 适配器
class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
对象适配器(组合)
class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
实际应用示例(电压适配器)
// 中国220V插座
class ChineseSocket {
public void provide220V() {
System.out.println("Providing 220V");
}
}
// 美国110V设备接口
interface USDevice {
void workOn110V();
}
// 适配器
class SocketAdapter implements USDevice {
private ChineseSocket socket;
public SocketAdapter(ChineseSocket socket) {
this.socket = socket;
}
@Override
public void workOn110V() {
socket.provide220V();
System.out.println("Converting 220V to 110V");
}
}
四、代理模式(Proxy Pattern)
原理
代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。
使用场景
远程代理(远程方法调用)
虚拟代理(延迟加载大对象)
保护代理(控制访问权限)
智能引用(引用计数、懒加载等)
优缺点
优点:
职责清晰
高扩展性
智能化
缺点:
增加系统复杂度
可能降低请求处理速度
实现代码
静态代理
interface Image {
void display();
}
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading " + filename);
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
动态代理(JDK)
interface Subject {
void request();
}
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject handling request");
}
}
class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call");
Object result = method.invoke(target, args);
System.out.println("After method call");
return result;
}
}
// 使用
Subject realSubject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class[]{Subject.class},
new DynamicProxyHandler(realSubject)
);
proxy.request();
Cglib代理
class RealService {
public void doSomething() {
System.out.println("RealService doing something");
}
}
class CglibProxy implements MethodInterceptor {
public Object getProxy(Class clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method call");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method call");
return result;
}
}
// 使用
RealService proxy = (RealService) new CglibProxy().getProxy(RealService.class);
proxy.doSomething();
五、对比
模式 | 目的 | 主要应用场景 | 关键特点 |
---|---|---|---|
单例 | 控制实例数量 | 全局唯一对象 | 私有构造、静态实例 |
工厂 | 封装对象创建 | 复杂对象创建 | 创建与使用分离 |
适配器 | 接口转换 | 兼容旧系统 | 包装不兼容接口 |
代理 | 控制访问 | 访问控制、增强功能 | 间接访问、功能增强 |