建造者模式是否存在潜在设计缺陷?方法设计合理性疑问
建造者模式两个设计疑问解答
为什么要单独设计getResult()方法?
- 核心目的是实现构建流程和最终产品的完全解耦。经典GoF建造者模式里,编排构建步骤的角色是
Director(指挥者),它只负责按固定顺序调用各个buildPart()方法,从设计上就不需要知道最终构建出来的是什么产品。同样一套构建流程,换个具体建造者实现,就可能产出完全不同类型的对象:比如同样执行buildTitle、buildBody、buildFooter三个步骤,HTML建造者返回HTML字符串,PDF建造者返回二进制PDF对象,Markdown建造者返回md格式文本。Director不需要处理这些差异,最终拿结果的动作是客户端发起的,getResult()就是留给客户端获取最终产物的统一入口。 - 其次是为了保证返回对象的完整性。复杂对象的构建是过程性的,中间状态的半成品大多是不可用的:比如造车只装了轮子没装发动机、做报表只填了表头没填数据,这些状态的对象如果暴露出去很容易引发业务问题。
getResult()可以在返回前做统一的合法性校验、资源初始化,确保交到客户端手里的一定是状态合法、可直接使用的完整对象。
为什么buildPart()方法没有返回值?这是不是冗余设计?
你觉得设计冗余,本质上是默认了“谁调用buildPart()谁就要拿结果”,但经典模式里调用buildPart()的是Director,它从职责划分上就不该拿到构建结果:
buildPart()的单一职责是修改建造者内部维护的产品实例状态,不需要对外暴露任何中间数据。如果给buildPart()加返回值,不管是返回建造者自身还是返回零件/半成品,都会强迫Director感知产品的内部结构:比如造车的建造者buildWheel()返回轮子对象,造房子的建造者buildWall()返回墙壁对象,那Director的流程代码就得针对不同产品做适配,彻底违背了“同一构建流程可复用生成不同产品”的核心设计目标。- 所谓“让
buildPart()直接返回构建结果更高效”,其实是把经典建造者模式和现在常用的流式建造者变体搞混了。日常开发里我们经常写的链式调用Builder(比如builder.setName("a").setAge(1).build()),是没有独立Director的场景下的简化实现:客户端自己充当Director编排构建步骤,这时候让构建方法返回建造者自身确实写起来更顺畅,但这只是特定场景下的优化,不是普适的设计准则。如果你的场景需要复用统一构建流程、支持多类型产品构建,无返回值的buildPart()反而是耦合最低的设计。
可以参考最基础的经典实现伪代码,直观看到各角色的职责划分:
// 抽象建造者:定义统一构建步骤,不绑定具体产品 abstract class Builder { public abstract void buildPartA(); public abstract void buildPartB(); public abstract Object getResult(); } // 具体建造者:自己维护内部产品实例,构建方法仅修改内部状态 class ConcreteBuilderA extends Builder { private ProductA product = new ProductA(); @Override public void buildPartA() { product.setFeatureA("A类产品的A部分"); } @Override public void buildPartB() { product.setFeatureB("A类产品的B部分"); } @Override public ProductA getResult() { product.checkValid(); // 最终合法性校验 return product; } } // 指挥者:只负责按顺序调用构建步骤,完全不接触具体产品实现 class Director { public void build(Builder builder) { builder.buildPartA(); builder.buildPartB(); } } // 客户端调用 public class Client { public static void main(String[] args) { Builder builder = new ConcreteBuilderA(); Director director = new Director(); director.build(builder); ProductA res = (ProductA) builder.getResult(); } }
从代码里就能看出来,Director的build方法完全不需要返回值,它的职责就是把流程跑完,拿结果是客户端的事,不存在“不拿结果为什么调用buildPart”的问题——调用buildPart本来就不是为了拿即时结果,是为了推进构建流程,最终结果本来就是等全流程跑完之后才需要获取的。
内容的提问来源于stack exchange,提问作者RedFox
相关产品推荐
相关产品推荐

