你突然看到某段代码用了工厂模式,第一反应可能是:有必要吗?直接new一个对象不行吗?干嘛「故意」增加阅读难度?
其实不是这样的,当你接触过的高手多了后,你会自然而然的认为:高手的代码,确实不太容易看懂,尤其你水平还不够的时候。
我不是说你菜,只是每个人都会经历过这个阶段的。关键在于,你得分清楚一件事:到底是代码本身写得烂,还是你的知识面暂时还覆盖不到这里。
你觉得没必要,可能是你还没遇到那个场景
如果你构造的对象只是简单地设置几个属性,确实没必要用工厂。直接用构造方法或者Builder就够了。
问题出在另一种场景:构造一个业务对象的过程本身就很复杂。
拿门店进销存系统中的物料盘点模块举例。创建一张盘点单据,需要做这些事情:
- 根据模板ID去查盘点模板,拿到模板里配置的名称、类型、盘点日期规则
- 调用编码生成服务,按照业务规则生成单据编号
- 把提交过来的物料数据逐条转换成明细实体
- 把以上所有数据组装成一个完整的聚合根对象
这不是简单地new一个对象然后set几个字段。整个构造过程涉及到外部服务调用、数据库查询、业务规则计算。如果这些逻辑散落在调用方的代码里,那每个需要创建盘点单据的地方都得重复写一遍。
用工厂模式封装这些构造逻辑,代码大概长这样:
Java
@Component
public class StocktakingDocsFactory {
private final CodeGeneratorService codeGeneratorService;
private final StocktakingTemplateRepository templateRepository;
public StocktakingDocsAggregateRoot create(SubmitStocktakingCommand command) {
// 查询模板,获取名称、类型、日期规则
StocktakingTemplate template = templateRepository.getById(command.getTemplateId());
// 生成单据编号
String code = codeGeneratorService.generate(ModuleEnum.STOCKTAKING);
// 组装明细
List<StocktakingItemEntity> items = buildItems(command.getMaterialList());
// 构建聚合根
return StocktakingDocsAggregateRoot.builder()
.code(code)
.name(template.getName())
.type(template.getType())
.items(items)
.build();
}
}
调用方只需要一行 factory.create(command) 就拿到了完整的聚合根对象。构造过程中涉及到的模板查询、编码生成、数据转换这些细节,全部被封装在工厂内部。
当你知道工厂模式就是干这个事情的时候,看到对应的代码会觉得很自然。 觉得绕,往往是因为你还没遇到过需要它的场景。
高手的代码都是精心组织过的
我带过的团队里,水平高的那几个人,写出来的代码有一个共性:都是有规划、有设计的。随意写代码,对他们来说是大忌。
你甚至会经常听到他们说这句话:不要这搞一个,那搞一个,要设计和规划的。
这基本是他们的口头禅了。
再看一个例子。库存系统里有一个库存调整的功能,不同的业务场景对库存的处理方式不一样:
- 履约扣减:只减理论库存,不动实际库存
- 期初入库:直接覆盖所有库存数量
- 盘点调整:先改实际库存,再同步理论库存
- 调拨/收货/报损:理论库存和实际库存同时增减
如果把这些逻辑全堆在一个方法里用if-else判断,那个方法会膨胀到几百行,而且每次新增一种场景都要在那坨if-else里再加一个分支。改的人心惊胆战,因为怕影响其他分支。
看看高手怎么组织这段逻辑。先定义一个策略接口:
Java
public interface InventoryAdjustmentHandler {
MaterialInventoryAggregateRoot execute(AdjustmentContext context);
boolean isSupported(AdjustmentContext context);
}
每种场景一个实现类。履约扣减的处理器:
Java
@Component
public class FulfillmentAdjustmentHandler implements InventoryAdjustmentHandler {
public static final List<StatementType> TYPES = List.of(
StatementType.PRODUCT_DEDUCTION, StatementType.PRODUCT_RETURNS);
@Override
public boolean isSupported(AdjustmentContext context) {
return TYPES.contains(context.getStatementType());
}
@Override
public MaterialInventoryAggregateRoot execute(AdjustmentContext context) {
// 只扣减理论库存
context.getAggregateRoot().fulfillmentAdjustment(context.getDTO());
return context.getAggregateRoot();
}
}
聚合根里的调用入口,通过Spring容器自动发现所有处理器,让它们自己判断是否适用:
Java
public MaterialInventoryAggregateRoot adjustment(InventoryAdjustmentDTO dto) {
Map<String, InventoryAdjustmentHandler> handlers =
SpringUtils.getContext().getBeansOfType(InventoryAdjustmentHandler.class);
handlers.forEach((key, handler) -> {
AdjustmentContext context = new AdjustmentContext(this, dto, statementType);
if (handler.isSupported(context)) {
handler.execute(context);
}
});
return this;
}
这种代码有一个特别重要的性质:它没那么容易被破坏。
新增一种库存调整场景,你只需要写一个新的实现类,实现 isSupported 和 execute 两个方法。已有的履约处理器、期初入库处理器、盘点处理器,一行代码都不用动。
新来的同事接手这段代码,他能破坏的范围被限制在他自己写的那个实现类里。他不需要理解其他场景的处理逻辑,也不会因为改错了一行代码导致其他场景出bug。
这就是我说的「精心组织过」的含义。不是为了炫技,是为了让代码在多人协作、长期维护的环境下,尽量少出问题。

水平提升之后,你会觉得他写得真好
这个认知转变的过程,大部分人都会经历。
一开始看到策略模式、工厂模式,觉得绕,觉得不直观。明明可以if-else写在一起,为什么要拆成这么多类?
等你自己维护过一个屎山项目,在一个2000行的方法里找了一下午bug,改一行代码导致三个不相关的功能挂掉之后,你对「代码组织」这件事的理解会完全不一样。
你会开始理解:那些拆成多个小类、每个类职责单一的代码,不是为了好看,是为了让维护者不那么痛苦。你甚至会发自内心地觉得那个人写得真好。
更关键的是,你自己后面也会慢慢写出那样的代码。不是刻意模仿,是你理解了背后的道理之后,自然而然就会那样去组织代码。
这说明你成长了。
从看不懂,到看懂了觉得好,再到自己也能写出来。这是每个开发者技术成长的必经之路。
怎么判断是代码烂还是你水平不够
有些代码确实写得烂,看不懂不是你的问题。怎么区分这两种情况?这张表可以帮你快速判断:
| 维度 | 代码确实写得烂 | 你水平还不够 |
|---|---|---|
| 命名 | 变量名abc、temp满天飞,类名和功能对不上 | 类名长但语义精确,比如FulfillmentInventoryAdjustmentHandler |
| 结构 | 一个方法500行,什么逻辑都往里塞 | 拆成多个小类,每个类只做一件事 |
| 模式 | 没有任何模式,全是if-else和重复代码 | 用了你不认识的设计模式(工厂、策略、模板方法等) |
| 修改成本 | 改一行可能影响十个功能 | 新增功能只需加一个新类,不用动已有代码 |
| 一致性 | 同样的事情三个地方三种写法 | 整个模块的代码风格、组织方式高度统一 |
如果你对照这张表,发现让你困惑的代码在右边这一列全中了:类名虽然长但意思明确,结构拆得很细但每个类职责单一,用了你没见过的模式但改动时不影响其他地方。
那大概率不是代码的问题,是你的知识储备还没覆盖到这里。这不丢人,补起来就好了。
小结
代码组织这件事,表面上看是个编码习惯的问题,深一层想,它反映的是一个开发者对「长期维护成本」的认知水平。
写代码最容易的方式永远是把所有逻辑堆在一起,这样写得最快,当下理解成本最低。代价是后面每次修改都如履薄冰,因为你不知道改这里会不会影响那里。
高手选择多花一些时间做代码的组织和设计,把逻辑拆开、把边界画清楚、把扩展点留好。短期看起来代码多了、类多了、看起来绕了。长期看,每次改动的风险被控制在一个很小的范围内。团队的人来来走走,代码依然能稳定地演进下去。
看不懂这种代码,不用焦虑。先去了解对应的设计模式,理解它要解决的问题,再回来看代码,感受完全不同。等你有一天自己也写出了这样的代码,回头看看自己当初的困惑,你会觉得那就是成长的印记。
最近在知乎出了「应付6000万会员的秒杀系统专栏」和「几亿用户,百万并发的C端商品系统实战」专栏,感兴趣的可以订阅一下。至于知识星球的,可以搜:
- 老码头的技术浮生录
它是一个能实际帮你解决难题的星球。有问题的,找知心的Sam哥,支持无限次语音一对一解决你遇到的难题。「另外后续我新写的所有对外的付费专栏,在星球内都是免费的,且可以拿到所有源代码。」
知识星球内后续将推出20+个付费专栏,覆盖电商全链路:
| 选购线 | 用户会员营销线 | 中后台 |
|---|---|---|
| 购物车服务 | 营销系统 | 订单系统 |
| 商品服务 | 用户系统 | 支付系统 |
| 菜单服务 | 结算服务 |
从前台选购到中后台结算,星球成员全部免费,后续新增也不额外收费。
我的知乎账号:
- SamDeepThinking