设计模式实战:单例、工厂、策略、责任链四模式全解析
2026/9/13 20:13:39 网站建设 项目流程

做技术这么多年,我经常被问到设计模式到底该怎么学。很多新手拿着《大话设计模式》从头啃到尾,合上书还是不知道代码里该用哪个。这次借着"设计模式大全"里最常用的单例、工厂、策略、责任链这四种模式,我把项目里真正用得上的思路、写法、坑点整理成一份能直接上手的技术笔记。这篇内容适合准备面试的同学、在项目里想优化代码结构的初中级开发,也适合带团队的负责人作为代码评审的依据。

我不会上来就甩一堆UML图,而是先用一句话说清楚每个模式到底解决了什么问题,再给出可直接落地的代码示例,最后聊到实际开发中我踩过的坑。先说这四种模式的共性思维:它们都是在解决"变化"带来的问题。把不变的骨架固定下来,把变化的部分抽出去封装好,让代码能组合、能扩展、能测试。所以整篇笔记,都可以围绕"找到变化、封装变化"这八个字来理解。

1. 整体设计:为什么是这四种模式,它们解决什么问题

1.1 设计模式背后的核心思想:程序里的"可插拔"

我刚学设计模式的时候有个误区,以为设计模式是一堆高大上的语法技巧。直到在真实项目里重构了三次订单模块之后才转过来:设计模式根本不是语法炫技,而是一套对象之间怎么协作的成熟套路。程序里的耦合就像插座和插头,设计模式就是把接口标准化,让不同的实现像家电一样随便插拔。

这四种模式放在一起,刚好覆盖了面向对象设计里最常见的四类职责分配场景。单例模式解决的是"一个类只能有一个实例,且要全局共享"的资源管理问题;工厂模式解决的是"客户端不该知道创建细节,想创建谁就创建谁"的创建解耦问题;策略模式解决的是"同一件事有不同的做法,运行时可以自由切换"的算法族替换问题;责任链模式解决的是"一个请求要被多个对象依次处理,但发送者不必知道是谁处理的"的调用方解耦问题。

把它们组合起来,几乎能应付后端业务开发里80%的重构需求。比如我在做支付渠道接入的时候,四个模式用在了同一套代码里:通过单例模式持有微信支付、支付宝支付、银联支付的客户端实例;通过工厂模式根据订单金额和用户所在地区选择创建对应的支付渠道服务;通过策略模式把不同的支付方式封装成统一接口,切换实现只需改一行配置;再把风控校验、幂等校验、签权校验串成一条责任链,任何一个环节不通过,请求就不会走到真正的支付下单逻辑。

1.2 如何判断一个场景该用哪种模式

很多人的困扰不是不知道怎么用,而是不知道什么时候用。这里有一个我总结的判断模型:先看你的代码里有没有"变化"的重心。如果你发现每次改动都要动同一个类、同一串if-else,说明这里就是变化点,就该拿模式去隔离开。

如果变化发生在"需要几个实例、实例怎么被共享",考虑单例;如果变化发生在"到底是哪个具体对象被创建",考虑工厂;如果变化发生在"同一个接口下一套算法互相替换",考虑策略;如果变化发生在"多个对象都有机会处理同一个请求,且处理顺序有关联",考虑责任链。

还有一个辅助判断:看你的类名后面有没有跟太多修饰语。比如"XXXManager""XXXUtil""XXXService"这样的类里通常混合了这四类逻辑点。遇到这种类,就该想一想,是不是该拆了。

1.3 学习路径建议:先会用,再谈理解

我不建议一上来就背GoF书里那23个模式的UML图。比较有效的路径是:先拿一段已经被if-else糊住的烂代码,用某个模式重构成干净的版本,感受一下重构前后的差别。这也就是为什么我把这四种模式按照"从最常用到最具结构性"的顺序来写,读者可以先从单例这种最不复杂的切入,用手感建立信心,再往责任链这种链条装配的模式深入。

另外建议在学习时配合一个真实的业务场景。比如用"下单支付"流程,把这四种模式都套进去练习,比毫无上下文地写Demo有用得多。下面进入正文,逐一说清每种模式。

2. 单例模式:从懒汉到枚举,线程安全的完整路线

2.1 单例模式的本质:全局唯一实例的"门禁"

单例模式看起来很简单,类图就一个类,私有构造方法,提供一个静态方法全局访问。我见过不少人觉得好像就是"静态变量+静态方法"而已,甚至直接写了一个全局对象就没再用单例。但单例的真正价值在于:它把"是否只有一个实例"这个约束封装在类内部,而不是靠约定、靠文档提醒大家都别new。

场景最有代表性的就是配置管理器、数据库连接池、线程池、日志管理器、Spring容器里的默认Bean。这些对象如果被new出多份,要么是资源被浪费,要么是数据一致性直接被破坏。比如日志管理器被多次实例化,不同地方打印的日志可能写到不同的文件句柄,排查问题的时候会发现日志缺了一段,非常头疼。

2.2 懒汉与饿汉:最常见的两派写法

单例实现里最经典的两种写法,我直接给出Java版本对比。

饿汉式:

public class EagerSingleton { // 类加载时即创建,天然线程安全 private static final EagerSingleton INSTANCE = new EagerSingleton(); private EagerSingleton() { // 防止外部通过构造器创建实例 } public static EagerSingleton getInstance() { return INSTANCE; } }

懒汉式(线程安全的双重检查锁版本):

public class DoubleCheckSingleton { // volatile保证多线程下的可见性和禁止指令重排序 private static volatile DoubleCheckSingleton instance; private DoubleCheckSingleton() { } public static DoubleCheckSingleton getInstance() { if (instance == null) { // 第一次检查,减少进入同步块的次数 synchronized (DoubleCheckSingleton.class) { if (instance == null) { // 第二次检查,防止多个线程同时创建 instance = new DoubleCheckSingleton(); } } } return instance; } }

饿汉式以牺牲启动时间为代价换取线程安全,类一旦加载就会初始化实例,不存在多线程竞争的问题。但如果有类被加载后一直不被使用,实例却被提前创建了,等于占着资源不用。懒汉式的双重检查锁是面试高频题,关键是那两次if和volatile,缺一不可。如果省掉volatile,在极端情况下,线程A执行完new操作但还没写入引用字段前,线程B可能读到一个半初始化的对象。

有人会问,加synchronized不就行了,为什么还要volatile?因为synchronized保证的是互斥访问,而volatile在这里保证的是"可见性"和"禁止重排序"。new操作在JVM里面至少有三步:分配内存、调用构造器初始化、把引用指向该内存。如果没有volatile,第二步和第三步可能被重排序,线程B拿到引用的时候对象可能还没构造完,直接使用会出问题。

2.3 更优雅的实现:静态内部类和枚举

在Java里,还有两种写法我推荐在项目里优先考虑。第一种是静态内部类:

public class InnerClassSingleton { private InnerClassSingleton() { } private static class Holder { private static final InnerClassSingleton INSTANCE = new InnerClassSingleton(); } public static InnerClassSingleton getInstance() { return Holder.INSTANCE; } }

这种方式利用类加载机制保证懒加载和线程安全,只有当getInstance()被调用时,静态内部类才被加载,实例才被创建。写起来简洁,性能也行,没有同步开销。

第二种是枚举:

public enum EnumSingleton { INSTANCE; public void doSomething() { // 业务方法 } }

用枚举实现单例可以说是"终极大招"。它天然防反射攻击、防序列化破坏。普通的单例类,通过反射你可以把构造方法设置成可访问,然后new出第二个实例;实现Serializable接口的话,反序列化时也可能得到一个新的实例。而枚举类型在JVM层面就保证了唯一性,这也是Effective Java作者Joshua Bloch反复推荐的方式。

2.4 C++场景下的线程安全单例

热词里出现了"c++ 单例模式 线程安全",因为C++的情况与Java有些不同。C++11之前,实现线程安全单例非常麻烦。C++11之后有了魔性静态变量初始化机制,可以在局部静态变量的首次初始化时由编译器保证线程安全,于是Meyers Singleton成为C++下最推荐的写法:

class Singleton { public: static Singleton& getInstance() { static Singleton instance; // C++11之后线程安全的懒汉式 return instance; } Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; private: Singleton() = default; };

这里把一个静态局部变量放在函数体内,C++11标准规定,多个线程同时首次调用这个函数时,局部静态变量的初始化只发生一次,而且是线程安全的。这是最简洁、性能最好的方案。

注意:C++里还有一个坑是单例的析构顺序,特别是多个单例互相引用时,析构顺序不确定会导致崩溃。所以建议单例尽量少直接依赖其他单例,能传参就传参。

2.5 单例模式的实际注意事项

在实际项目中用单例,我总结了几条经验:

第一,单例不推荐放进接口或作为参数传递。单例本身就是全局的,如果到处传来传去,反而模糊了它"全局唯一"的本质,也不利于测试替换。

第二,如果你是写测试的,单例是一个让人头疼的东西。因为单例一旦创建就很难被重新初始化。我在测试配置类的时候,会用反射或者专门的test hook去重置实例,麻烦是麻烦了点,但总比每次跑用例都要重启JVM强。

第三,很多人误以为"全局变量就是单例"。这是错误的。全局变量只是能用,但没法防止别人再new一份,也没法保证初始化时机。单例的价值在于"强制唯一性",而全局变量只是"碰巧唯一"。

3. 工厂模式:从简单工厂到抽象工厂,一次说完三种形态

3.1 工厂模式解决什么问题:别让client直接new

工厂模式的核心可以浓缩成一句话:创建对象的过程不能散落在客户端代码里。为什么?因为一旦创建过程包含复杂的参数组装、配置加载、依赖初始化等等,每次new都是在重复这段逻辑,一旦创建方式发生变化,比如加了参数或者换了实现类,所有调用点都要跟着改。

打个比方,你去餐厅吃饭不需要知道后厨怎么洗菜、切菜、配调料,你只要对服务员说"来一份宫保鸡丁",后厨自然会按流程做好端出来。客户端代码就应该是这个"点菜的人",只说需求,不碰创建细节。

这里需要注意,热词里还有一个"海信液晶电视怎么进工厂模式",这跟设计模式完全是两码事。电视的工厂模式是电视系统里用于调试和工程测试的隐藏菜单入口,不是我们代码里说的工厂模式,大家别把两者的概念混淆了。本文提到的工厂模式,都是面向对象设计里创建对象的那一系列手法。

3.2 简单工厂:最入门,但违背开闭原则

先看一段最常见的简单工厂写法:

public class PaymentFactory { public static Payment create(String channel) { if ("wechat".equals(channel)) { return new WechatPay(); } else if ("alipay".equals(channel)) { return new Alipay(); } else if ("unionpay".equals(channel)) { return new UnionPay(); } throw new IllegalArgumentException("未知支付渠道: " + channel); } }

简单工厂不是GoF 23种设计模式之一,但它非常普及,因为它最简单。用一个静态方法通过参数返回不同的产品实例。问题也很明显:如果新增一个支付渠道,就要改这个静态方法,违背了开闭原则(对扩展开放,对修改关闭)。如果渠道少、变化不频繁,简单工厂完全够用。但一旦渠道数量膨胀到10个以上,这个工厂方法就会变成又长又臭的if-else链。

3.3 工厂方法模式:把工厂抽象化,各产品各配一家工厂

工厂方法模式的思路是把"工厂"这个概念抽象出来,每种产品对应一个具体的工厂类:

public interface PaymentFactory { Payment create(); } public class WechatPayFactory implements PaymentFactory { @Override public Payment create() { // 微信支付特有初始化,比如配置appId、商户号 return new WechatPay(); } } public class AlipayFactory implements PaymentFactory { @Override public Payment create() { // 支付宝特有初始化,比如配置应用ID、私钥 return new Alipay(); } }

客户端只需要依赖PaymentFactory接口,在配置里指定具体使用哪个工厂,或通过依赖注入把工厂实例传进来。新增渠道时新增一个XxxPayFactory类就好,老的工厂类一个不用动。

这里的关键变化:简单工厂把"选择分支"放在工厂里面,工厂方法模式把"选择分支"交给了客户端的装配。客户端仍然需要通过配置文件或ApplicationContext来决定用哪家工厂,但是创建逻辑本身被封死在各家工厂里了。

3.4 抽象工厂模式:围绕产品族的一致性好帮手

抽象工厂模式比工厂方法模式再进一步,但不建议一上来就强上。简单来说,工厂方法模式的每个工厂创建一种产品;抽象工厂模式的每个工厂要保证"同一系列"的产品能配套生产。

我用一个更贴近实际的后端场景来解释。假设你在做一个跨数据库的ORM配置模块,需要同时支持MySQL和PostgreSQL。每个数据库都有三种对象:连接池、方言(Dialect)、类型转换器(DataMapper)。为了保证不出现"MySQL连接池配了PostgreSQL的方言"这种混乱,就可以引入抽象工厂:

public interface DatabaseFactory { ConnectionPool createConnectionPool(); SqlDialect createSqlDialect(); ResultSetMapper createResultSetMapper(); } public class MySqlDatabaseFactory implements DatabaseFactory { @Override public ConnectionPool createConnectionPool() { return new MySqlConnectionPool(); } @Override public SqlDialect createSqlDialect() { return new MySqlDialect(); } @Override public ResultSetMapper createResultSetMapper() { return new MySqlMapper(); } }

抽象工厂最大的特点是"族约束"。通过接口的返回类型,编译器就能保证同一系列的产品一起出现,不会出现组合错乱。代价是接口一旦设计完成,要支持新的产品维度就非常困难。比如上面这个DatabaseFactory想加一个TransactionManager,所有实现类都得改。所以抽象工厂适用于产品族比较稳定的场景,新增一个产品族容易,新增一类产品维度难。

3.5 工厂模式的代码组织技巧

最后补充一些工厂模式落地时的经验。不要接过多的初始化参数。我见过有人把工厂方法设计成十几二十个参数,谁调用谁崩溃。更好的方式是把参数收拢成一个Config对象,或者是工厂根据环境变量/配置中心动态决定参数。

工厂类的职责要单一,创建完对象就返回,不要在工厂里顺手做业务校验。工厂里做业务校验会让工厂对自己生产出来的产品有过多隐性的假设,将来换实现的时候容易发现校验逻辑对不上。

如果项目用的是Spring,其实Spring的@Bean方法本身就是一个工厂。在配置类里声明一个个@Bean方法,返回统一接口类型,实际返回哪种实现由配置或条件注解决定。这比手写Factory类要省事得多,推荐优先考虑。

4. 策略模式:干掉项目中90%的if-else

4.1 策略模式的核心思想:算法可以作为参数传递

策略模式是我在代码评审时最喜欢推荐给团队的一个模式,因为它对代码可读性的提升极其明显。它的核心思想是:把某个算法或行为封装成独立的策略对象,使它们可以互相替换,且替换行为不影响客户端。

生活化地理解,就好比旅游。去同一个地方,你可以坐飞机、坐火车、自驾。目的地一样,但出行方式可以随时切换,而且你不需要改动"旅游"这个流程本身,只需要在出发前选一个交通工具。策略模式就是把"旅游"流程和被选中"交通工具"解耦了。

4.2 一个促销活动里的策略模式示例

假设你有电商项目,商品有满减、折扣、无优惠三种价格计算规则。不用策略模式时,一堆if-else如下:

public BigDecimal calculatePrice(String promotionType, BigDecimal amount) { if ("DISCOUNT".equals(promotionType)) { return amount.multiply(new BigDecimal("0.8")); } else if ("FULL_REDUCTION".equals(promotionType)) { if (amount.compareTo(new BigDecimal("100")) >= 0) { return amount.subtract(new BigDecimal("20")); } return amount; } else if ("NONE".equals(promotionType)) { return amount; } throw new IllegalArgumentException("未知促销类型"); }

一旦新增一种"满100减20再打九折"的玩法,这个方法的if-else又会增加一条。测试时也要把所有可能性完整过一遍。

用策略模式重构之后:

public interface PriceStrategy { BigDecimal calculate(BigDecimal amount); String type(); } @Component public class DiscountStrategy implements PriceStrategy { @Override public BigDecimal calculate(BigDecimal amount) { return amount.multiply(new BigDecimal("0.8")); } @Override public String type() { return "DISCOUNT"; } } @Component public class FullReductionStrategy implements PriceStrategy { @Override public BigDecimal calculate(BigDecimal amount) { if (amount.compareTo(new BigDecimal("100")) >= 0) { return amount.subtract(new BigDecimal("20")); } return amount; } @Override public String type() { return "FULL_REDUCTION"; } }

然后在促销上下文里把策略收拢起来:

@Service public class PriceCalculator { private final Map<String, PriceStrategy> strategyMap; public PriceCalculator(List<PriceStrategy> strategies) { strategyMap = strategies.stream() .collect(Collectors.toMap(PriceStrategy::type, Function.identity())); } public BigDecimal calculate(String promotionType, BigDecimal amount) { PriceStrategy strategy = strategyMap.get(promotionType); if (strategy == null) { throw new IllegalArgumentException("未知促销类型: " + promotionType); } return strategy.calculate(amount); } }

这段代码里使用Spring的依赖注入把所有PriceStrategy实现类收集到一个列表里,再转成Map。新增一种促销策略,只需要写一个新的@Component实现类,不用改任何现有业务代码,完全符合开闭原则。

4.3 策略模式与状态模式的区别

很多人分不清策略模式和状态模式,这两个名字听起来都很像"换一个对象解决不同情况"。关键区别在于"谁决定切换"。

策略模式下,客户端主动选择策略,策略对象不知道其它策略的存在,策略也不关系自己什么时候被调用。状态模式下,状态之间也会互相切换,状态对象知道自己执行结束后下一个状态是谁,切换不由外部主导,而是由状态内部转移。

举个例子:订单状态流转走到"已支付"状态时自动推进到"已发货"状态,这是状态模式。而客户在这个接口里选择"微信支付"还是"支付宝支付",这是策略模式。

4.4 策略模式适用场景和局限性

策略模式特别适合的情况:一个接口有多个实现、大家的行为互不相干、运行时要动态选。日志记录格式切换、支付渠道切换、排序规则封装、校验规则集合打包,这些我都用过。

策略模式也有它的限度,不要为了消掉if-else而强行上策略。如果分支只有两三个,且不太可能扩展,直接用二元运算符或简单的if也完全可以,过度设计比if-else更糟糕。另外策略类如果数量过多(比如几十个),管理成本会上升。这时候可以再引入工厂模式,把策略创建和选择逻辑统一在一个工厂里面,让业务代码只面对一个策略入口。

5. 责任链模式:把请求交到一条流水线上

5.1 责任链模式的核心思想:每个环节有各自的分工

责任链模式把多个处理对象串成一条链,请求沿着链向后传递,直到被某个对象处理或者完全通过。它适合处理系统的"验证拦截类"逻辑,比如登录校验、参数校验、权限校验、风控审核、多级审批。

我最早理解这个模式是通过请假流程:员工提交请假申请,先由组长审批,如果请假天数小于3天,组长批了就结束了;超过3天还要经理审批;超过10天还要VP审批。这个流程就是一条典型的责任链,申请人根本不需要知道自己到底会走到哪一级,他只需要把请假单递出去。

5.2 链的硬编码实现

一个最直接的责任链实现:每个Handler内部持有对下一个Handler的引用,处理完自己职责后决定是交给下一个还是结束流程。

public abstract class Approver { protected Approver next; public void setNext(Approver next) { this.next = next; } public abstract void approve(Request request); } public class LeaderApprover extends Approver { @Override public void approve(Request request) { if (request.getDays() <= 3) { System.out.println("组长审批通过"); } else if (next != null) { next.approve(request); } } }

这种写法直截了当,但缺点是链的组装散落在客户端代码里,每次调用都需要把LeaderApproverManagerApproverVpApprover挨个setNext串起来。如果团队里不同模块对链的要求不一样,组装代码将面临大量复制粘贴。

5.3 更灵活的实现:把链的装配交给容器

在Spring环境里,我通常会换一种写法:用一个处理器列表代替手写next引用,通过@Order注解控制执行顺序。这样做的好处是把"链的拼接"从手写setNext变成框架自动装配。

public interface ApproverHandler { void handle(RequestContext context); } @Component @Order(1) public class LeaderHandler implements ApproverHandler { @Override public void handle(RequestContext context) { if (context.getDays() <= 3) { context.setApproved(true); System.out.println("组长审批通过"); } } } @Component @Order(2) public class ManagerHandler implements ApproverHandler { @Override public void handle(RequestContext context) { if (!context.isApproved() && context.getDays() <= 10) { context.setApproved(true); System.out.println("经理审批通过"); } } }

组装逻辑由Spring容器把列表注入进来,业务代码只需要循环调用:

public class ApproverChain { private final List<ApproverHandler> handlers; public ApproverChain(List<ApproverHandler> handlers) { this.handlers = handlers; } public void execute(RequestContext context) { for (ApproverHandler handler : handlers) { handler.handle(context); if (context.isApproved()) { break; } } } }

这种实现和重写ArrayList类似,用一个列表模拟链表,好处是顺序很容易调整,只要修改@Order注解的值就行。坏处是没法做太复杂的链终止条件,只能是"通过就break"。不过大多数业务场景这样已经够用。

5.4 责任链在框架层面的应用:Filter、Interceptor和中间件

在Java Web框架里,责任链模式早就是底层基础设施了。Servlet的Filter、SpringMVC的Interceptor、Netty的ChannelPipeline都长着责任链的脸。我在排查线上一个问题时突然意识到:一个HTTP请求从进入Tomcat到被Controller处理,中间可能会经过身份认证Filter、参数解析Filter、日志Filter、限流Filter等等,这就是一条活生生的责任链。

所以学责任链模式的好处不只是用来应付面试题,更重要的是你能看懂框架源码里那层又一层"配置类"到底在做什么。当你理解了Handler、FilterChain这些概念之后,自己写中间件、写插件系统时就会非常顺手。

5.5 责任链模式要注意的坑

最大的坑是链变得太深太长。链每增加一环,请求的耗时就会增加,调试时也要多跳几步。我建议把职责相似的处理逻辑合并成一个Handler,不要为了追求链的完整而硬拆。

第二个坑是异常处理。责任链模式天然适合顺序执行,但如果某一个环节抛异常,后面的环节就执行不到了。有些场景这没问题,比如参数校验类;有些场景需要即使前面失败后面也要记录日志,比如风控统计,这时就要做成catch-and-continue,或者用不同的接口拆分出"校验型Handler"和"统计型Handler"。

第三个坑是难以定位问题。链路一旦很长,某个请求在哪里被拦截、在哪里被篡改,排查起来都麻烦。我的习惯是在每个Handler的入口和出口打一行日志,打印请求ID,方便跟踪链路。这也是为什么很多框架里都有"调用链中间件"的原因。

6. 四种模式如何选型:一张表说清使用边界

6.1 模式对比速查表

写到最后,我整理一张速查表,方便后续做技术选型时直接参考:

模式核心解决维度典型场景核心缺点常用实现语言
单例模式实例唯一配置管理器、连接池、线程池单例生命周期与测试耦合Java/C++/Python
工厂模式创建过程解耦支付渠道创建、数据库方言创建类数量暴增Java/C++/Python
策略模式算法族切换价格计算、排序规则、格式封装策略过多时管理成本高Java/Python
责任链模式请求多级处理校验、审批、过滤器、中间件链路过长难调优Java/Python

这张表把四种模式放在一起看重心的区别很直观。单例是"一个",工厂是"创建",策略是"可替换算法",责任链是"依次处理"。实际项目里这四种往往组合出现,比如工厂创建出来的对象内部又使用了策略模式,策略的选择器又在责任链里被调用,这些都是正常的,设计模式本来就是积木,可以互相嵌套。

6.2 组合使用的实例参考

不妨设想一个商品下单场景:单例的库存服务控制全局库存扣减并发安全;工厂根据商品类目创建不同的计价策略;计价策略中的折扣逻辑通过策略模式支撑不同会员等级;扣减库存前的一连串前置校验封装成责任链。这样四个模式各司其职,代码逻辑非常清晰。

6.3 不要过度设计的忠告

前面说了很多模式的好处,这里我得泼一盆冷水。设计模式是用来解决复杂度升级带来的维护问题的,但别为了用模式而用模式。如果你当前页面只有三五百行代码,需求也不会快速膨胀,直接写顺序逻辑可能是最清晰的选择。真要抽象出十几个接口、十几个类,反而增加阅读成本。

我在评审代码时有一条标准:如果新来的开发需要花超过30分钟才能理解你为什么要拆出这么多类,那就说明设计过度了。优秀的设计是这个度:让经验丰富的工程师觉得清晰,让新人花10分钟能懂,让改动需求时只动一个地方。这比学一套完美模式更有价值。

6.4 最后一句话的心得

用了这么多年设计模式,我最大的体会是:不要把它当成面试题库去背,要把它当成"代码坏味道"的治疗方案去理解。看到if-else太长就想想策略;看到到处new同一个对象就想想工厂;看到要求全局唯一就想想单例;看到一段请求要被反复过滤就想想责任链。描述代码遇到的问题,然后找一个对症下药的模式,你就能自然记住这些设计思路,不需要死记硬背。

另外,如果你在跟踪相关热词时看到"大话设计模式"这类书,建议看完每章后亲手用一个真实业务场景重写一遍,只在项目代码里跑过一遍的模式,才真正拿到生产环境里能用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询