设计模式深度解析:单例、工厂、适配器与代理模式

目录

[一、单例模式(Singleton Pattern)](#一、单例模式(Singleton Pattern))

原理

使用场景

优缺点

优点:

缺点:

实现代码

基础实现(非线程安全)

线程安全实现(加锁)

Java枚举实现(最佳实践)

[二、工厂模式(Factory Pattern)](#二、工厂模式(Factory Pattern))

原理

使用场景

优缺点

优点:

缺点:

实现代码

简单工厂

工厂方法

抽象工厂

[三、适配器模式(Adapter Pattern)](#三、适配器模式(Adapter Pattern))

原理

使用场景

优缺点

优点:

缺点:

实现代码

类适配器(继承)

对象适配器(组合)

实际应用示例(电压适配器)

[四、代理模式(Proxy Pattern)](#四、代理模式(Proxy Pattern))

原理

使用场景

优缺点

优点:

缺点:

实现代码

静态代理

动态代理(JDK)

Cglib代理


设计模式是软件开发中解决常见问题的可复用方案。本文将深入探讨四种常用的设计模式:单例模式、工厂模式、适配器模式和代理模式,分析它们的原理、使用场景、优缺点,并提供实现代码示例。

一、单例模式(Singleton Pattern)

原理

单例模式确保一个类只有一个实例,并提供一个全局访问点。它通过私有化构造函数、提供静态获取实例方法来实现。

使用场景

  • 需要控制资源访问,如数据库连接池

  • 全局配置对象

  • 日志记录器

  • 设备驱动程序

优缺点

优点:

  • 严格控制实例数量

  • 全局访问方便

  • 避免频繁创建销毁对象

缺点:

  • 违反单一职责原则(既管理生命周期又处理业务)

  • 难以扩展

  • 多线程环境下需要特殊处理

实现代码

基础实现(非线程安全)

java 复制代码
public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

线程安全实现(加锁)

java 复制代码
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枚举实现(最佳实践)

java 复制代码
public enum EnumSingleton {
    // 唯一实例
    INSTANCE;

    // 可以有任何字段或方法
    private int counter = 0;

    public void doSomething() {
        counter++;
        System.out.println("counter = " + counter);
    }
}
/**
 *  天生线程安全
 *    枚举常量在类加载阶段由 JVM 保证只实例化一次,且是原子操作;
 *    不需要任何同步代码,也永远不存在并发问题。
 *
 */

二、工厂模式(Factory Pattern)

原理

工厂模式定义一个创建对象的接口,但让子类决定实例化哪个类。它将对象创建与使用分离。

使用场景

  • 系统需要独立于其产品的创建、组合和表示

  • 需要提供产品的类库,只暴露接口

  • 需要灵活扩展系统时

优缺点

优点:

  • 解耦对象创建与使用

  • 易于扩展新产品

  • 符合开闭原则

缺点:

  • 增加了系统复杂度

  • 需要引入额外类

实现代码

简单工厂

java 复制代码
// ====== 简单工厂 ======
// 简单工厂:把"创建对象"的逻辑集中在一个类里,根据传入的 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 {}

工厂方法

java 复制代码
// ====== 工厂方法 ======
// 工厂方法:把"创建对象"推迟到子类实现,父类只定义流程骨架
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();
    }
}

抽象工厂

java 复制代码
// ====== 抽象工厂 ======
// 抽象工厂:一次性创建"一族"相关或依赖的产品(这里为 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)

原理

适配器模式将一个类的接口转换成客户期望的另一个接口,使原本不兼容的类可以一起工作。

使用场景

  • 需要使用现有类,但其接口不符合要求

  • 需要创建可复用的类,与不相关或不可预见的类协同工作

  • 需要使用几个子类,但子类化每个类不现实

优缺点

优点:

  • 使不兼容的接口协同工作

  • 提高类的复用性

  • 灵活、可扩展

缺点:

  • 过多使用会使系统混乱

  • 增加系统复杂度

实现代码

类适配器(继承)

java 复制代码
// 目标接口
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();
    }
}

对象适配器(组合)

java 复制代码
class ObjectAdapter implements Target {
    private Adaptee adaptee;
    
    public ObjectAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    
    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

实际应用示例(电压适配器)

java 复制代码
// 中国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)

原理

代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。

使用场景

  • 远程代理(远程方法调用)

  • 虚拟代理(延迟加载大对象)

  • 保护代理(控制访问权限)

  • 智能引用(引用计数、懒加载等)

优缺点

优点:

  • 职责清晰

  • 高扩展性

  • 智能化

缺点:

  • 增加系统复杂度

  • 可能降低请求处理速度

实现代码

静态代理

java 复制代码
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)

java 复制代码
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代理

java 复制代码
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();

五、对比

模式 目的 主要应用场景 关键特点
单例 控制实例数量 全局唯一对象 私有构造、静态实例
工厂 封装对象创建 复杂对象创建 创建与使用分离
适配器 接口转换 兼容旧系统 包装不兼容接口
代理 控制访问 访问控制、增强功能 间接访问、功能增强
相关推荐
Zane19945 天前
一个 new 就能创建对象,为什么还要拆出单例、工厂、建造者、原型四种模式?
设计模式
怕浪猫6 天前
一行命令复刻爆款视频,我把 Hypit 从安装跑到了出片
人工智能·设计模式·程序员
Zane19946 天前
函数式编程里的函数,其实不是你天天写的那个函数——三大编程范式的边界在哪
设计模式
Zane19947 天前
策略模式现在该不该上?一次讲清楚过度设计和设计不足怎么找平衡
设计模式
她说..7 天前
常见设计模式-模板方法模式
java·spring·设计模式·springboot
xiaofeiyang1508 天前
第六章 · 桥接 — 三支毛笔,画出九种颜色
设计模式
IPdodo_8 天前
住宅 IP 与数据中心 IP 到底差在哪:用 RDAP、BGP 和多源证据做工程判定
网络·网络安全·代理模式·ip
Shadow(⊙o⊙)8 天前
OTOL设计模式 One Thread One Loop
服务器·网络·设计模式
sarasuki9 天前
如何让LLM 能在半夜偷偷打开网易云呢?
人工智能·设计模式·agent
小王师傅669 天前
【设计模式】装饰模式(四):框架源码实战——从 Java I/O 到 Spring 到 MyBatis
java·设计模式