主要设计模式:单例、工厂、适配器、代理

发布于:2025-07-17 ⋅ 阅读:(19) ⋅ 点赞:(0)

设计模式:单例、工厂、适配器代理

在软件开发中,设计模式是经过验证的“最佳实践”,能够帮助开发者解决重复出现的问题,提升代码的可维护性、可扩展性和可读性。本文将聚焦四种常用设计模式——单例模式工厂模式适配器模式代理模式,从核心原理到实战场景,带你全面掌握它们的用法与精髓。

一、单例模式

定义与核心价值

单例模式(Singleton Pattern)是一种创建型设计模式,它保证一个类在整个应用中只有一个实例,并提供一个全局访问点。这种模式的核心价值在于避免重复创建对象导致的资源浪费(如数据库连接、配置文件加载),同时确保全局状态的一致性。

核心解决问题

  • 避免频繁创建销毁重量级对象(如线程池、缓存);
  • 控制全局共享资源的访问(如日志器、配置管理器);
  • 确保系统中某个类的实例唯一性(如全局计数器)。

实现方式与代码示例

单例模式的实现需注意线程安全懒加载(按需创建),常见方式有以下几种:

  1. 饿汉式:类加载时直接创建实例,线程安全但可能提前占用资源。
public class HungrySingleton {
    // 类加载时初始化,天然线程安全
    private static final HungrySingleton INSTANCE = new HungrySingleton();
    
    // 私有构造器阻止外部实例化
    private HungrySingleton() {}
    
    // 全局访问点
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}
  1. 懒汉式(双重检查锁定):延迟创建,通过同步锁和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)是结构型设计模式,它将一个类的接口转换成客户端期望的另一个接口,使原本因接口不兼容而无法协作的类能够一起工作。

核心解决问题

  • 整合遗留系统:老接口与新接口不匹配;
  • 复用第三方组件:第三方库接口与本地系统不一致;
  • 避免大规模修改现有代码:通过适配层隔离变化。

实现方式

  1. 类适配器:通过继承目标接口和适配者类实现(单继承语言限制较多);
  2. 对象适配器:通过组合适配者对象实现(更灵活,推荐使用)。

代码示例:播放器适配器

假设现有一个只能播放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框架中通过代理调用远程服务;
  • 资源优化:延迟加载大对象(如高清图片);
  • 功能增强:在调用前后添加日志、事务、缓存等逻辑。

实现方式

  1. 静态代理:手动创建代理类,与目标类实现同一接口;
  2. 动态代理:运行时动态生成代理类(如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(); 
    }
}

总结:模式的选择与协同

单例、工厂、适配器、代理四种模式虽用途不同,但核心都是解耦

  • 单例模式解耦“全局唯一实例”的创建与使用;
  • 工厂模式解耦“对象创建”与“业务逻辑”;
  • 适配器模式解耦“新旧接口”的兼容性问题;
  • 代理模式解耦“核心功能”与“增强逻辑”。

在实际开发中,模式常组合使用:例如,工厂模式创建的对象可通过代理模式添加日志适配器模式适配的接口可由单例模式管理全局访问。选择模式时需避免过度设计,根据业务复杂度灵活取舍——简单场景用简单工厂,复杂扩展用工厂方法,接口不兼容用适配器,需增强控制用代理。


网站公告

今日签到

点亮在社区的每一天
去签到