恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Java设计模式实战:从单例到策略,代码实现与避坑指南

  • 首页
  • 资讯中心
  • /
  • Java设计模式实战:从单例到策略,代码实现与避坑指南

相关资讯

FPGA高速收发器GT核心原理与实战调试指南 2026/8/6 4:50:10
3分钟掌握:palera1n iOS越狱完整指南与实用技巧 2026/8/6 4:45:09
C语言小结---2048游戏实现 2026/8/6 4:45:09

最新资讯

Windows本地搭建Pikachu靶场:PHPStudy环境配置与Web安全实战指南
IAR嵌入式开发入门:从环境配置到项目实战全解析
STM32定时器输入捕获:从原理到实战,精准测量信号脉宽与频率
Apache Paimon流式湖仓实践:统一流批处理,构建实时数据管道
时序模型全解析:从静态分析到动态预测的核心原理与实践
STM32定时器输入捕获:从原理到实战,精准测量脉宽与频率

今日推荐

电力系统调度中的源荷不确定性建模与优化实践
VGG-T3技术解析:3D重建速度的革命性突破
深度解析旅游网站建设的意义及其对行业发展的深远影响与核心价值体现

本周热门

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案
分布式配置中心选型实战:Nacos与Consul在创业场景下的对比
MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

Java设计模式实战:从单例到策略,代码实现与避坑指南

发布时间:2026/8/6 4:50:10
Java设计模式实战:从单例到策略,代码实现与避坑指南 1. 项目概述为什么我们要从代码实现开始聊设计模式聊到设计模式很多刚入行的朋友可能会觉得有点“虚”。书上的UML图看懂了每个模式的定义也背了但一到自己写代码还是不知道该怎么用或者用起来总觉得别扭生搬硬套。这就是典型的“理论和实践脱节”。我见过不少项目为了用模式而用模式把简单的逻辑搞得异常复杂类图画出来挺漂亮代码维护起来却让人头疼。所以这个系列我们不空谈理论就从一个资深开发者的视角聚焦于如何用Java代码干净、落地、恰到好处地实现这些模式并分享在真实项目中应用它们时的取舍与心得。设计模式不是银弹它更像是一套经过验证的“招式库”。高手对决时不会刻意去想“我现在要用第几式”而是根据对手的出招业务需求的变化自然使出最合适的应对。我们的目标就是通过具体的代码实现帮你把这些“招式”内化让你在面临需求变更、系统扩展时能下意识地写出更灵活、更健壮的代码。本系列将覆盖创建型、结构型和行为型三大类模式每一篇都会深入1-2个模式用最直白的代码和最常见的业务场景说清楚“怎么实现”以及“为什么这么实现”。2. 核心思路从场景到代码的落地路径在动手写代码之前我们必须明确一个核心原则模式服务于业务而非业务将就模式。我的思路是“场景驱动”先构想一个简单但典型的业务场景然后观察这个场景在演化过程中暴露出的设计问题最后引入设计模式作为解决方案。这样你就能清晰地看到模式带来的价值而不是觉得它凭空增加了复杂度。2.1 场景驱动的学习法举个例子假设我们最初有一个简单的任务创建一个“通知器”能发送邮件。你可能会写一个EmailNotifier类里面一个send方法。这没问题。但很快产品经理说“我们还要支持短信通知。” 于是你加了SmsNotifier类。接着又要支持微信推送、App站内信…… 你会发现每次新增一种通知方式你都要去修改调用通知的客户端代码或者写一堆if-else来判断。这时你就遇到了“变化点”而“工厂模式”或“策略模式”可能就是你的解药。我们将遵循“定义场景 - 暴露痛点 - 引入模式 - 代码重构 - 对比优劣”的路径。每一段代码都会有“模式前”和“模式后”的对比让你直观感受改进在哪里。2.2 代码实现的评判标准实现一个模式代码可以运行只是最低要求。我认为好的实现需要满足以下几点符合开闭原则这是衡量模式应用是否成功的关键。系统应该对扩展开放对修改关闭。新增功能时是否只需添加新类而无需改动现有稳定代码客户端调用足够简单模式内部的复杂是为了外部的简单。客户端代码不应该感知到模式内部复杂的对象创建或组装过程。命名清晰意图明确类名、方法名要能直接反映其职责和模式角色如XxxFactory,XxxStrategy,XxxAdapter。避免过度设计如果当前场景很简单未来变化可能性极低直接写简单的代码反而更优。模式是工具不是枷锁。3. 创建型模式实战单例模式 (Singleton) 的深潜与避坑单例模式大概是所有人学到的第一个设计模式概念简单一个类只有一个实例并提供一个全局访问点。但它的实现细节和坑点足以写满好几页纸。我们不止步于“双重检查锁定”而要深入其变体、原理及现代最佳实践。3.1 场景与痛点为什么需要单例想象一个应用配置管理器ConfigManager。它在系统启动时从文件或数据库加载配置耗时操作之后在程序的任何地方各个模块都需要读取同一份配置信息。如果每个模块都自己new一个ConfigManager实例不仅会产生多次不必要的IO消耗还可能因为加载时机不同导致配置不一致。这时我们就需要确保全局只有一个ConfigManager实例。模式前的糟糕代码// 模块A ConfigManager configA new ConfigManager(); String dbUrl configA.get(database.url); // 模块B ConfigManager configB new ConfigManager(); String cacheHost configB.get(cache.host); // configA 和 configB 是不同实例可能加载了不同的配置内容造成混乱。3.2 经典实现与演进1. 饿汉式 (Eager Initialization)public class ConfigManager { // 1. 私有静态实例类加载时就初始化 private static final ConfigManager INSTANCE new ConfigManager(); // 2. 私有构造器防止外部new private ConfigManager() { // 模拟耗时的配置加载 loadConfigFromFile(); } // 3. 公共静态方法提供全局访问点 public static ConfigManager getInstance() { return INSTANCE; } private void loadConfigFromFile() { System.out.println(Loading configuration...); // 实际从文件读取配置 } }注意优点是实现简单线程安全由JVM类加载机制保证。缺点是无论你用不用实例都会在类加载时创建如果实例初始化非常耗时或占用资源多可能会拖慢应用启动速度是一种“以空间换时间”的思想。2. 懒汉式 (Lazy Initialization) 与线程安全public class ConfigManager { private static ConfigManager INSTANCE; // 不再用final private ConfigManager() {} // 非线程安全版本 public static ConfigManager getInstance() { if (INSTANCE null) { INSTANCE new ConfigManager(); // 多线程下可能创建多个实例 } return INSTANCE; } }非线程安全的懒汉式是面试常考点但在生产环境是绝对禁止的。为了解决线程安全问题我们引入同步锁。3. 双重检查锁定 (Double-Checked Locking)public class ConfigManager { // 使用 volatile 关键字确保多线程下的可见性和禁止指令重排序 private static volatile ConfigManager INSTANCE; private ConfigManager() {} public static ConfigManager getInstance() { if (INSTANCE null) { // 第一次检查避免不必要的同步 synchronized (ConfigManager.class) { if (INSTANCE null) { // 第二次检查确保唯一性 INSTANCE new ConfigManager(); } } } return INSTANCE; } }实操心得这里的volatile关键字至关重要。INSTANCE new ConfigManager();这行代码并非原子操作它包含1. 分配内存空间2. 初始化对象3. 将引用指向内存地址。JVM可能进行指令重排序导致其他线程拿到一个未初始化完全的对象空壳。volatile可以防止这种重排序保证线程安全。这是面试高频深水区务必理解。4. 静态内部类实现 (Holder) —— 当前最推荐写法public class ConfigManager { private ConfigManager() {} // 静态内部类 private static class SingletonHolder { private static final ConfigManager INSTANCE new ConfigManager(); } public static ConfigManager getInstance() { return SingletonHolder.INSTANCE; // 只有调用此方法时内部类才会被加载INSTANCE被创建 } }这是我最喜欢也是项目中最常用的一种方式。它利用了JVM的类加载机制静态内部类SingletonHolder只有在getInstance()方法第一次被调用时才会被加载和初始化其静态字段INSTANCE。这个过程由JVM保证线程安全性且实现了懒加载。代码简洁性能高效无需担心同步问题。5. 枚举实现 (Enum) —— Joshua Bloch 大神推荐public enum ConfigManager { INSTANCE; // 可以在此添加实例方法 public void loadConfig() { // ... } } // 使用ConfigManager.INSTANCE.loadConfig();这是《Effective Java》作者极力推荐的方式。它不仅能避免多线程同步问题还能防止反序列化和反射攻击创建新的实例。缺点是它并非一种“懒加载”枚举实例在类被加载时就会初始化。对于大部分场景这微小的启动开销是可以接受的换来了绝对的安全和简洁。3.3 单例模式的常见陷阱与反思序列化与反序列化破坏单例如果一个单例类实现了Serializable接口反序列化时会创建一个新的实例。解决方法是在类中添加readResolve()方法。protected Object readResolve() { return getInstance(); }反射攻击通过反射调用私有构造器可以创建新实例。枚举单例可以天然防御此攻击。对于类实现可以在构造器中加入判断逻辑。private ConfigManager() { if (INSTANCE ! null) { throw new RuntimeException(Use getInstance() method to get the single instance.); } }单例与单元测试的困境单例的全局状态会使单元测试变得困难测试之间可能相互影响。一个实用的技巧是尽量为单例类定义接口并在生产代码中使用依赖注入的方式获取其实例虽然实例仍是单例但在测试时可以方便地替换为Mock对象。是否真的需要单例这是最该问自己的问题。单例本质上是一个“全局变量”它带来了便利也引入了耦合和测试难度。在Spring这类IoC容器管理的应用中将Bean的作用域定义为singleton由容器管理生命周期是比手写单例模式更优雅、更现代的做法。手写单例模式更适用于基础框架、工具类或尚未引入容器的轻量级应用。4. 创建型模式实战工厂方法 (Factory Method) 与抽象工厂 (Abstract Factory) 的抉择工厂模式家族用于封装对象的创建过程将客户端代码与具体产品类解耦。工厂方法和抽象工厂容易混淆我们通过对比来厘清。4.1 工厂方法模式处理单一产品等级结构场景一个日志记录器框架需要支持将日志写入不同目的地文件、数据库、控制台。未来可能扩展至Kafka、Elasticsearch。痛点如果不使用模式客户端代码中会充斥大量的if-else或switch根据配置或参数来new不同的记录器对象。每次新增记录器类型都要修改客户端代码违反开闭原则。工厂方法模式实现 定义一个创建对象的接口但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。// 1. 产品接口 public interface Logger { void log(String message); } // 2. 具体产品 public class FileLogger implements Logger { Override public void log(String message) { System.out.println(Log to file: message); } } public class DatabaseLogger implements Logger { Override public void log(String message) { System.out.println(Log to database: message); } } // 3. 创建者抽象类核心 public abstract class LoggerFactory { // 这就是“工厂方法” public abstract Logger createLogger(); // 可以包含一些与产品相关的核心业务逻辑 public void log(String message) { Logger logger createLogger(); // 调用工厂方法解耦 logger.log(message); } } // 4. 具体创建者 public class FileLoggerFactory extends LoggerFactory { Override public Logger createLogger() { // 这里可以包含复杂的初始化逻辑比如打开文件句柄 return new FileLogger(); } } public class DatabaseLoggerFactory extends LoggerFactory { Override public Logger createLogger() { // 初始化数据库连接等 return new DatabaseLogger(); } } // 客户端代码 public class Client { public static void main(String[] args) { LoggerFactory factory new FileLoggerFactory(); // 可通过配置读取 factory.log(An important event.); // 要切换为数据库日志只需改为new DatabaseLoggerFactory() } }模式价值客户端 (Client) 只依赖LoggerFactory抽象和Logger抽象。当需要新增一个KafkaLogger时我们只需新增KafkaLogger产品和KafkaLoggerFactory创建者无需修改任何现有工厂和客户端代码。系统实现了对扩展开放。4.2 抽象工厂模式处理多个产品族场景升级上面的日志系统。现在日志不仅分目的地还分格式简单文本格式和JSON格式。我们需要创建“文件文本日志”、“文件JSON日志”、“数据库文本日志”、“数据库JSON日志”等一系列相关或依赖的对象。痛点如果还用工厂方法我们需要为“文件文本”、“文件JSON”分别创建工厂类会爆炸式增长且难以保证“文件”工厂创建的产品一定是文件相关的。抽象工厂模式实现 提供一个接口用于创建相关的或依赖对象的家族而不需要明确指定具体类。// 1. 抽象产品族格式 public interface LogFormatter { String format(String message); } public class TextFormatter implements LogFormatter { Override public String format(String m) { return [TEXT] m; } } public class JsonFormatter implements LogFormatter { Override public String format(String m) { return {\msg\: \ m \}; } } // 2. 抽象产品族写入器 public interface LogWriter { void write(String content); } public class FileWriter implements LogWriter { Override public void write(String c) { System.out.println(Write to File: c); } } public class DatabaseWriter implements LogWriter { Override public void write(String c) { System.out.println(Write to DB: c); } } // 3. 抽象工厂核心 public interface LoggerAbstractFactory { LogFormatter createFormatter(); LogWriter createWriter(); } // 4. 具体工厂每个工厂负责一个产品族 public class FileTextFactory implements LoggerAbstractFactory { Override public LogFormatter createFormatter() { return new TextFormatter(); } Override public LogWriter createWriter() { return new FileWriter(); } } public class FileJsonFactory implements LoggerAbstractFactory { Override public LogFormatter createFormatter() { return new JsonFormatter(); } Override public LogWriter createWriter() { return new FileWriter(); } } public class DatabaseTextFactory implements LoggerAbstractFactory { Override public LogFormatter createFormatter() { return new TextFormatter(); } Override public LogWriter createWriter() { return new DatabaseWriter(); } } // 5. 一个组装好的“日志器” public class AdvancedLogger { private LogFormatter formatter; private LogWriter writer; public AdvancedLogger(LoggerAbstractFactory factory) { this.formatter factory.createFormatter(); this.writer factory.createWriter(); // 保证formatter和writer来自同一家族 } public void log(String message) { String formatted formatter.format(message); writer.write(formatted); } } // 客户端代码 public class Client { public static void main(String[] args) { LoggerAbstractFactory factory new FileJsonFactory(); // 配置决定 AdvancedLogger logger new AdvancedLogger(factory); logger.log(User login); // 输出Write to File: {msg: User login} // 要切换为数据库文本日志只需更换工厂new DatabaseTextFactory() } }核心区别与选择工厂方法关注单一产品的创建。FileLoggerFactory只负责创建FileLogger。解决“单个对象”的创建扩展问题。抽象工厂关注产品族的创建。FileJsonFactory负责创建一整套能协同工作的产品 (JsonFormatterFileWriter)。解决“一系列相互关联对象”的创建扩展问题并保证产品间的兼容性。如何选如果你的系统中有多个属于不同产品等级结构如电器中的空调、冰箱但属于同一家族如海尔、格力的产品需要一起创建就用抽象工厂。如果只是要创建一种产品如各种风格的按钮用工厂方法更简单。5. 结构型模式实战适配器模式 (Adapter) 的两种实现与真实用例适配器模式就像电源转接头让原本接口不兼容的两个类可以协同工作。它分为类适配器继承和对象适配器组合两种后者更常用也更符合组合优于继承的原则。5.1 场景整合遗留系统假设我们有一个现代支付系统定义了一个统一的支付接口ModernPaymentGatewaypublic interface ModernPaymentGateway { boolean pay(BigDecimal amount, String currency); }系统里已经实现了CreditCardProcessor和PayPalProcessor。现在公司收购了一个老项目里面有一个古老的支付类LegacyPaymentService它的方法长这样public class LegacyPaymentService { // 方法名、参数顺序、类型都不同 public boolean makePayment(int cents) { System.out.println(Legacy system processing: cents cents); return true; } }我们需要让新的系统能够调用这个老的服务。直接修改LegacyPaymentService风险高也不符合开闭原则。适配器登场。5.2 对象适配器实现推荐通过组合的方式持有被适配者的引用。public class LegacyPaymentAdapter implements ModernPaymentGateway { // 组合一个被适配对象 private LegacyPaymentService legacyService; public LegacyPaymentAdapter(LegacyPaymentService legacyService) { this.legacyService legacyService; } Override public boolean pay(BigDecimal amount, String currency) { // 在这里进行适配逻辑 // 1. 货币转换假设只处理USD if (!USD.equalsIgnoreCase(currency)) { throw new UnsupportedOperationException(Legacy system only supports USD.); } // 2. 金额单位转换元转分 int cents amount.multiply(new BigDecimal(100)).intValue(); // 3. 调用被适配者的方法 return legacyService.makePayment(cents); } }客户端使用public class Client { public static void main(String[] args) { ModernPaymentGateway gateway new LegacyPaymentAdapter(new LegacyPaymentService()); boolean success gateway.pay(new BigDecimal(99.99), USD); System.out.println(Payment result: success); } }优点灵活。LegacyPaymentAdapter可以适配LegacyPaymentService及其任何子类。符合“组合优于继承”原则。5.3 类适配器实现需要多重继承Java中不直接支持类适配器通过继承被适配者来实现。在Java中由于单继承限制如果被适配者是一个类且适配器需要同时继承它和实现目标接口这只有在适配器本身不需要继承其他类时才可行。如果被适配者是类且目标也是类不是接口则无法实现。因此在Java里当被适配者是一个具体类时对象适配器是唯一选择。如果被适配者也是一个接口则可以模拟但意义不大。假设LegacyPaymentService是一个接口ILegacyPayment// 假设的接口 public interface ILegacyPayment { boolean makePayment(int cents); } public class ConcreteLegacyService implements ILegacyPayment { ... } // 类适配器在Java中这要求Target是接口Adaptee也是接口或类 public class ClassAdapter extends ConcreteLegacyService implements ModernPaymentGateway { Override public boolean pay(BigDecimal amount, String currency) { int cents amount.multiply(new BigDecimal(100)).intValue(); return super.makePayment(cents); // 直接调用父类方法 } }注意由于Java单继承ClassAdapter已经继承了ConcreteLegacyService就无法再继承其他类了灵活性受限。因此在绝大多数Java场景下优先使用对象适配器。5.4 适配器模式在JDK和Spring中的身影JDKjava.util.Arrays#asList(T... a)可以看作一个适配器它将一个数组适配成了List接口。SpringSpring MVC 中的HandlerAdapter是适配器模式的经典应用。不同的Controller如基于Controller注解的、实现Controller接口的有不同的处理方式。DispatcherServlet通过HandlerAdapter来适配各种Controller使得Servlet可以以统一的方式调用它们。实际心得适配器模式常用于系统集成和接口升级。在微服务架构中调用外部第三方服务时经常为其编写一个“Client Adapter”将第三方千奇百怪的API封装成符合我们内部规范的接口这样核心业务逻辑就与第三方实现解耦了。未来更换第三方服务时只需换一个Adapter即可。6. 行为型模式实战策略模式 (Strategy) 的动态之美策略模式定义了算法家族分别封装起来让它们之间可以互相替换。此模式让算法的变化独立于使用算法的客户端。6.1 场景多种促销折扣计算电商平台有各种促销策略无折扣、固定折扣、百分比折扣、满减等。而且这些策略经常变动和增加。痛点如果使用if-else或switch在订单计算类里硬编码每次新增或修改策略都要修改这个核心计算类容易出错且违反开闭原则。// 糟糕的代码 public class OrderService { public BigDecimal calculateDiscount(String type, BigDecimal amount) { if (FIXED.equals(type)) { return amount.subtract(new BigDecimal(10)); // 减10元 } else if (PERCENT.equals(type)) { return amount.multiply(new BigDecimal(0.9)); // 9折 } else if (OVER100MINUS20.equals(type)) { if (amount.compareTo(new BigDecimal(100)) 0) { return amount.subtract(new BigDecimal(20)); } return amount; } // ... 更多else if return amount; } }6.2 策略模式实现将“做什么”和“怎么做”分离// 1. 策略接口 public interface DiscountStrategy { BigDecimal applyDiscount(BigDecimal originalAmount); } // 2. 具体策略 public class NoDiscountStrategy implements DiscountStrategy { Override public BigDecimal applyDiscount(BigDecimal amount) { return amount; } } public class FixedDiscountStrategy implements DiscountStrategy { private BigDecimal discountAmount; public FixedDiscountStrategy(BigDecimal discountAmount) { this.discountAmount discountAmount; } Override public BigDecimal applyDiscount(BigDecimal amount) { return amount.subtract(discountAmount); } } public class PercentageDiscountStrategy implements DiscountStrategy { private BigDecimal percentage; // 0.8 表示8折 public PercentageDiscountStrategy(BigDecimal percentage) { this.percentage percentage; } Override public BigDecimal applyDiscount(BigDecimal amount) { return amount.multiply(percentage); } } public class OverThresholdDiscountStrategy implements DiscountStrategy { private BigDecimal threshold; private BigDecimal discount; public OverThresholdDiscountStrategy(BigDecimal t, BigDecimal d) { this.threshold t; this.discount d; } Override public BigDecimal applyDiscount(BigDecimal amount) { return (amount.compareTo(threshold) 0) ? amount.subtract(discount) : amount; } } // 3. 上下文 (Context) - 负责使用策略 public class DiscountContext { private DiscountStrategy strategy; // 设置策略 public void setStrategy(DiscountStrategy strategy) { this.strategy strategy; } // 执行策略 public BigDecimal calculate(BigDecimal amount) { if (strategy null) { throw new IllegalStateException(Discount strategy not set.); } return strategy.applyDiscount(amount); } } // 客户端代码 public class Client { public static void main(String[] args) { DiscountContext context new DiscountContext(); BigDecimal orderAmount new BigDecimal(150); // 动态切换策略 context.setStrategy(new FixedDiscountStrategy(new BigDecimal(10))); System.out.println(Fixed: context.calculate(orderAmount)); // 140 context.setStrategy(new PercentageDiscountStrategy(new BigDecimal(0.8))); System.out.println(80% off: context.calculate(orderAmount)); // 120 context.setStrategy(new OverThresholdDiscountStrategy(new BigDecimal(100), new BigDecimal(25))); System.out.println(Over 100 minus 25: context.calculate(orderAmount)); // 125 } }6.3 策略模式与工厂模式的结合在实际项目中我们通常不会在客户端手动new具体的策略对象。而是结合工厂模式或Spring的依赖注入来管理策略的创建。// 策略工厂 public class DiscountStrategyFactory { private static final MapString, DiscountStrategy strategies new HashMap(); static { strategies.put(FIXED_10, new FixedDiscountStrategy(new BigDecimal(10))); strategies.put(PERCENT_90, new PercentageDiscountStrategy(new BigDecimal(0.9))); strategies.put(OVER_100_20, new OverThresholdDiscountStrategy(new BigDecimal(100), new BigDecimal(20))); // 可以从配置或数据库加载策略参数动态创建 } public static DiscountStrategy getStrategy(String strategyCode) { DiscountStrategy strategy strategies.get(strategyCode); if (strategy null) { throw new IllegalArgumentException(Unknown strategy code: strategyCode); } return strategy; } } // 在Spring环境中可以更优雅地使用 Component 和 Autowired Service public class OrderService { Autowired private MapString, DiscountStrategy strategyMap; // Spring会自动将所有DiscountStrategy实现注入为Mapkey是bean name public BigDecimal calculateOrder(String strategyCode, BigDecimal amount) { DiscountStrategy strategy strategyMap.get(strategyCode); if (strategy null) { strategy new NoDiscountStrategy(); } return strategy.applyDiscount(amount); } }核心优势消除条件判断客户端代码如OrderService中不再有冗长的if-else只需通过一个标识符获取策略并执行。符合开闭原则新增一种折扣策略只需新增一个DiscountStrategy的实现类并在工厂或Spring容器中注册即可。OrderService的calculateOrder方法完全不需要修改。便于测试每个策略都是独立的类可以单独进行单元测试。OrderService也可以轻松地用Mock策略进行测试。策略可共享策略类通常是无状态的只有行为没有成员变量可以被多个上下文共享实例减少对象创建开销。7. 模式应用中的共性陷阱与最佳实践经过上面几个模式的代码演练我们可以总结出一些跨模式的通用经验和容易踩的坑。7.1 警惕过度设计和模式堆砌这是新手最容易犯的错误。看到一点“变化”就想用模式导致简单问题复杂化。判断标准问自己这个变化发生的频率高吗如果一年都改不了一次用简单的if-else可能更可读、更易维护。或者未来扩展的可能性是否足够清晰如果需求还很模糊先使用简单实现等模式真正需要时再重构Refactoring to Patterns。反面案例一个只有两种导出格式Excel, CSV的报告功能非要用上抽象工厂、建造者、模板方法三个模式创建了十几个类。这就是典型的过度设计。7.2 理解模式的本质而非死记结构所有模式的根本目的都是解耦和应对变化。不要纠结于类图是否和《设计模式》书上一模一样。只要你的代码实现了“将变化的部分封装起来”、“针对接口编程”、“组合优于继承”这些原则即使类名不同、结构略有差异你也是在正确地使用模式思想。例如如果某个“策略”非常简单只有一两行代码使用Java 8以后的Lambda表达式或方法引用可能比创建一个完整的策略类更简洁。// 传统策略类 context.setStrategy(new DiscountStrategy() { Override public BigDecimal applyDiscount(BigDecimal amount) { return amount.multiply(new BigDecimal(0.95)); } }); // Lambda简化 context.setStrategy(amount - amount.multiply(new BigDecimal(0.95)));7.3 结合现代框架和语言特性在Spring Boot项目中很多模式已经被框架以更优雅的方式实现了。单例Spring管理的Bean默认就是单例Scope(“singleton”)无需手写。工厂Spring的ApplicationContext就是一个巨大的工厂Bean注解就是在定义产品Autowired就是在获取产品。策略如前所述利用MapString, Strategy的自动注入可以极其方便地管理策略。模板方法Spring的JdbcTemplate,RestTemplate等都是模板方法模式的典范它们定义了骨架我们只需提供回调如RowMapper。最佳实践是优先使用框架提供的机制当框架不满足或你在编写框架底层、工具类时再考虑手写经典的设计模式实现。7.4 命名至关重要好的命名是“活文档”。使用模式时在类名中体现其角色能极大提升代码的可读性。XxxFactory一看就知道是创建Xxx的工厂。XxxAdapter一看就知道是适配Xxx的适配器。XxxStrategy一看就知道是Xxx的策略。XxxBuilder一看就知道是构建Xxx的建造者。 避免使用XxxImpl,XxxHelper这种模糊的名字。写代码时多思考“这段代码未来最可能因为什么而修改”。找到那个“变化点”然后用合适的设计模式去封装它这才是设计模式应用的真正起点。在接下来的系列文章中我们会继续深入其他经典模式比如观察者模式如何优雅处理事件驱动、责任链模式如何构建灵活的审批流程、代理模式如何实现AOP等继续用扎实的代码和真实的场景把每个模式讲透、用活。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号