当前位置: 首页 > java >正文

设计模式:软件开发的高效解决方案(单例、工厂、适配器、代理)

什么是设计模式?

设计模式(Design Pattern)是软件开发中经过验证的、可复用的解决方案,用于解决特定场景下的通用问题。它不是具体的代码实现,而是一套被反复使用的 “设计思路” 或 “模板”,总结了前人在面对同类问题时的最佳实践。

设计模式的意义

  1. 解决共性问题开发中常遇到重复出现的问题,设计模式提供了标准化的解决方案,避免重复造轮子。
  2. 提升代码质量遵循设计模式的代码通常更符合高内聚、低耦合的原则,具备更好的:
  • 可复用性(代码片段可在不同场景复用);
  • 可维护性(结构清晰,便于修改);
  • 可扩展性(新增功能时无需大幅修改原有代码)。

设计模式的基本分类(经典23种)

创建型模式(5 种)
关注对象创建机制,解决 “如何灵活创建对象” 的问题,避免直接使用 new 导致的耦合。
  • 单例模式(Singleton):确保类只有一个实例;
  • 工厂模式(Factory):统一管理对象创建,解耦创建与使用;
  • 抽象工厂模式(Abstract Factory):创建一系列相关对象;
  • 建造者模式(Builder):分步构建复杂对象(如配置类);
  • 原型模式(Prototype):通过复制已有对象创建新实例(如克隆)。
结构型模式(7 种)
关注类与对象的组合关系,解决 “如何构建灵活的系统结构” 的问题。
  • 适配器模式(Adapter):让不兼容的接口协同工作(如老系统对接新组件);
  • 代理模式(Proxy):控制对对象的访问(如权限校验、延迟加载);
  • 装饰器模式(Decorator):动态给对象添加功能(如给日志增加加密功能);
  • 桥接模式(Bridge):分离抽象与实现,让两者独立变化(如跨平台组件);
  • 组合模式(Composite):将对象组合成树形结构,统一处理单个对象和组合对象(如文件目录);
  • 外观模式(Facade):为复杂系统提供简化接口(如封装第三方 SDK 的调用);
  • 享元模式(Flyweight):复用相似对象,减少内存消耗(如游戏中的重复角色模型)。
行为型模式(11 种)
关注对象间的交互与职责分配,解决 “如何让对象高效协作” 的问题。
  • 观察者模式(Observer):对象状态变化时,自动通知依赖它的其他对象(如事件监听);
  • 策略模式(Strategy):定义多种算法,动态切换(如支付方式:微信 / 支付宝);
  • 模板方法模式(Template Method):定义流程骨架,子类实现具体步骤(如报表生成流程);
  • 迭代器模式(Iterator):统一遍历集合的方式(如 for-each 循环);
  • 命令模式(Command):将请求封装为对象,实现 “请求 - 执行” 解耦(如撤销操作);
  • 责任链模式(Chain of Responsibility):多个对象依次处理请求(如权限审批流程);
  • 状态模式(State):对象状态变化时自动改变行为(如订单状态:待支付→已支付→已发货);
  • 其他:访问者模式、中介者模式、备忘录模式、解释器模式。

一、单例模式

单例模式解决的核心问题?
单例模式(Singleton Pattern)解决的核心问题是:如何保证一个类在整个应用中只有一个实例,并提供全局统一的访问点。在软件开发中,某些类型的对象(如配置管理器、日志工具、线程池、数据库连接池等)如果存在多个实例,会导致严重问题.
定义与核心要素
单例模式(Singleton)是创建型模式,它确保一个类仅有一个实例,并提供一个全局访问点。其核心要素包括:
  • 私有构造函数(禁止外部直接创建对象);
  • 类内部维护唯一实例;
  • 提供公开静态方法获取实例。
常见实现方式
1. 饿汉式
public class ConfigManager {    // 类加载时直接初始化实例(“饿”体现为提前创建)    private static final ConfigManager INSTANCE = new ConfigManager();        // 私有构造函数,禁止外部创建    private ConfigManager() {}        // 全局访问点    public static ConfigManager getInstance() {        return INSTANCE;    }
}
优点:实现简单,类加载时初始化,天然线程安全;
缺点:若实例未被使用,会浪费内存(适合肯定会用到的场景)。
2. 懒汉式(双重检查锁,按需创建)
public class Logger {    // volatile 确保多线程下实例可见性//只有静态变量才能被静态方法调用    private static volatile Logger INSTANCE;//私有构造函数,不允许外部创建对象        private Logger() {}        public static Logger getInstance() {        // 第一次检查:所有线程都能读到,未初始化才进入同步块       if (INSTANCE == null) {            // 同步块:避免多线程同时创建            synchronized (Logger.class) {                // 第二次检查:防止同步块内重复创建                if (INSTANCE == null) {                    INSTANCE = new Logger();                   }            }        }        return INSTANCE;    }
}
优点:按需初始化,节省内存;
注意:必须用 volatile 修饰实例,避免指令重排序导致的 “半初始化” 问题。
应用场景与注意事项
  • 适用场景:全局配置、线程池、缓存、日志系统等;
  • 注意事项
  • 避免在单例中存储可变状态(可能导致线程安全问题);
  • 序列化 / 反序列化时需重写 readResolve() 方法,防止创建新实例。

二、工厂模式

工厂模式解决的核心问题
        当一个类直接通过 new 创建另一个类的对象时,两者会紧密耦合 —— 若被创建的类发生变化(如构造函数修改、类名变更),所有使用 new 的地方都需要修改。工厂模式通过 “工厂类” 统一管理对象创建,实现 “创建” 与 “使用” 的分离
工厂模式的定义与分类
        工厂模式(Factory)是创建型模式,它定义一个用于创建对象的接口,让子类决定实例化哪个类。常见分类包括:
  • 简单工厂(非标准模式,适合简单场景);
  • 工厂方法(核心模式,本文重点讲解);
  • 抽象工厂(处理多产品族场景)。
工厂方法模式的实现
以 “日志记录器” 为例:系统需要支持 “文件日志” 和 “数据库日志”,且未来可能扩展 多种日志。
1. 定义产品接口与具体产品
// 日志产品接口
public interface Logger {void log(String message);
}
// 文件日志(具体产品)
public class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("文件日志:" + message);}
}
// 数据库日志(具体产品)
public class DatabaseLogger implements Logger {@Overridepublic void log(String message) {System.out.println("数据库日志:" + message);}
}
2. 定义工厂接口与具体工厂
// 日志工厂接口
public interface LoggerFactory {Logger createLogger();
}
// 文件日志工厂
public class FileLoggerFactory implements LoggerFactory {@Overridepublic Logger createLogger() {// 可在此添加文件初始化逻辑(如创建日志文件)return new FileLogger();}
}
// 数据库日志工厂
public class DatabaseLoggerFactory implements LoggerFactory {@Overridepublic Logger createLogger() {// 可在此添加数据库连接逻辑return new DatabaseLogger();}
}
3. 客户端使用(无需关心具体实现)
public class Client {public static void main(String[] args) {// 若需切换日志类型,只需修改工厂实现LoggerFactory factory = new FileLoggerFactory(); Logger logger = factory.createLogger();logger.log("系统启动成功");}
}
工厂模式优势与适用场景
  • 解耦:客户端只需依赖工厂接口,无需知道具体产品的创建细节;
  • 扩展性:新增产品(如 ConsoleLogger)时,只需新增对应的工厂类,符合 “开闭原则”;
  • 适用场景:对象创建复杂(如需要初始化资源)、产品类型多变(如支付方式:微信、支付宝)的场景。

三、适配器模式

适配器模式解决的核心问题
适配器模式解决的问题是:如何让不兼容的接口一起工作。
在系统升级或集成第三方组件时,常遇到 “老接口” 与 “新接口” 不兼容的问题。
例如:老系统的支付接口是 pay(double amount),而新引入的支付 SDK 接口是 doPayment(BigDecimal money)—— 直接修改老系统风险太高,此时适配器模式可作为 “中间层” 解决冲突。
适配器模式定义与分类
适配器模式(Adapter)是结构型模式,它将一个类的接口转换成客户端期望的另一个接口,使原本因接口不匹配而无法工作的类可以协同工作。分为:
  • 类适配器(通过继承实现,Java 因单继承限制较少用);
  • 对象适配器(通过组合实现,更灵活,推荐)。
对象适配器的实现
以 “电压适配” 为例:中国电器使用 220V 电压,而美国电器需要 110V 电压,需通过适配器转换。
1. 目标接口(客户端期望的接口)
// 目标接口:美国电器需要 110V 电压
public interface UsVoltage {int provide110V();
}
2. 被适配者(现有接口)
// 适配器:将 220V 转换为 110V
public class VoltageAdapter implements UsVoltage {// 组合被适配者private ChinaPower chinaPower;public VoltageAdapter(ChinaPower chinaPower) {this.chinaPower = chinaPower;}@Overridepublic int provide110V() {int original = chinaPower.provide220V();return original / 2; // 转换电压}
}
3. 适配器(连接目标与被适配者)
// 适配器:将 220V 转换为 110V
public class VoltageAdapter implements UsVoltage {// 组合被适配者private ChinaPower chinaPower;public VoltageAdapter(ChinaPower chinaPower) {this.chinaPower = chinaPower;}@Overridepublic int provide110V() {int original = chinaPower.provide220V();return original / 2; // 转换电压}
}
4. 客户端使用
public class UsAppliance {public static void main(String[] args) {// 被适配者:中国电源ChinaPower power = new ChinaPower();// 适配器:转换电压UsVoltage adapter = new VoltageAdapter(power);// 美国电器使用适配后的电压System.out.println("美国电器获得电压:" + adapter.provide110V() + "V");}
}
优势与适用场景
  • 兼容性:无需修改原有代码即可重用老组件;
  • 灵活性:通过组合实现,可适配多个被适配者;
  • 适用场景:系统集成、第三方组件适配、旧系统升级。

四、代理模式

代理模式解决的核心问题?
代理模式主要解决的核心问题:如何控制对对象的访问(如延迟加载、权限校验)
有些对象的创建或访问成本很高(如大图片、远程服务),直接使用可能导致性能问题;或需要在访问前进行权限校验、日志记录等操作。代理模式可在不修改原对象的前提下,对其访问进行控制。
代理模式定义与分类
代理模式(Proxy)是结构型模式,它为另一个对象提供一个代理,并由代理对象控制对原对象的访问。常见分类:
  • 静态代理(编译期确定代理类);
  • 动态代理(运行期生成代理类,如 JDK 动态代理、CGLIB)。
动态代理的实现(JDK 动态代理)
以 “图片加载” 为例:大型图片加载耗时,需通过代理实现 “延迟加载”(用到时才真正加载)。
1. 定义业务接口与真实对象
// 图片接口
public interface Image {void display();
}
// 真实图片(加载耗时)
public class RealImage implements Image {private String filename;public RealImage(String filename) {this.filename = filename;loadFromDisk(); // 初始化时加载图片}private void loadFromDisk() {System.out.println("从磁盘加载图片:" + filename);}@Overridepublic void display() {System.out.println("显示图片:" + filename);}
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
2. 实现动态代理处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 代理处理器:控制对真实对象的访问
public class ImageProxyHandler implements InvocationHandler {private String filename;private RealImage realImage; // 真实对象(延迟初始化)public ImageProxyHandler(String filename) {this.filename = filename;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 延迟加载:第一次调用 display 时才创建真实对象if (realImage == null) {realImage = new RealImage(filename);}// 调用真实对象的方法return method.invoke(realImage, args);}// 创建代理对象public static Image createProxy(String filename) {return (Image) Proxy.newProxyInstance(Image.class.getClassLoader(),new Class[]{Image.class},new ImageProxyHandler(filename));}
}
3. 客户端使用
public class Client {public static void main(String[] args) {// 创建代理对象(此时未加载图片)Image image = ImageProxyHandler.createProxy("large_pic.jpg");System.out.println("代理对象已创建,但未加载图片");// 第一次调用 display 时,代理会触发真实图片加载image.display();// 第二次调用:直接使用已加载的真实对象image.display();}
}
优势与适用场景
  • 控制访问:可实现延迟加载、权限校验、日志记录、事务管理等;
  • 无侵入性:无需修改原对象代码,符合 “开闭原则”;
  • 适用场景:远程服务调用(如 RPC)、AOP 切面(如 Spring 的 @Transactional)、资源密集型对象的延迟加载。

五、四种模式的对比与选择

模式
类型
核心作用
典型场景
单例模式
创建型
确保唯一实例,全局访问
配置管理、线程池
工厂模式
创建型
解耦对象创建与使用
多产品创建(如日志、支付方式)
适配器模式
结构型
解决接口不兼容问题
系统集成、旧接口适配
代理模式
结构型
控制对象访问(增强功能)
延迟加载、权限控制、AOP
http://www.xdnf.cn/news/15308.html

相关文章:

  • 预处理器完整功能介绍和示例演示(LESS/SCSS)
  • DMDIS文件到数据库
  • 并查集 UnionFind Test01
  • 什么是RAG(Retrieval-Augmented Generation)?一文读懂检索增强生成
  • websocket连接时发生未知错误
  • SAP顾问职位汇总(第28周)
  • 快速生成 Android 的 Splash 的 9 Patch 图片
  • phpMyAdmin:一款经典的MySQL在线管理工具又回来了
  • DNS解析过程和nmap端口扫描
  • 【STM32实践篇】:F407 时钟系统
  • MacOS使用Multipass快速搭建轻量级k3s集群
  • Spring Boot 安全登录系统:前后端分离实现
  • ERA5的UV合并成矢量并按时间维度转为nc或tif
  • 【版本控制】Perforce Helix Core (P4V) 完全入门指南(含虚幻引擎实战)
  • Spring Boot 集成 Spring Security 完整示例
  • C++ 单例模式实现
  • 牛客周赛 Round 100
  • AB实验评估指标体系之【实验评估指标体系】
  • 【Linux | 网络】应用层(HTTP)
  • RabbitMQ 之仲裁队列
  • 决策树的相关理论学习
  • 慢慢理解this
  • Dify离线安装包-集成全部插件、模板和依赖组件,方便安可内网使用
  • Matlab批量转换1km降水数据为tiff格式
  • 业务访问控制-ACL与包过滤
  • Qt窗口:QToolBar、QStatusBar、QDockWidget、QDialog
  • vue3 ref vs reactive值的修改
  • es里为什么node和shard不是一对一的关系
  • Git 使用笔记
  • 使用Starrocks替换Clickhouse的理由