ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

建造者模式详解:分步构建复杂对象

建造者模式详解:分步构建复杂对象 一、建造者模式定义Builder 模式是一种对象创建型模式用来隐藏复合对象的创建过程。它把复合对象的创建过程加以抽象通过子类继承与重载的方式动态地创建具有复合属性的对象。一句话当你要创建的对象由多个部件组成、且构建步骤固定时用建造者把怎么装和装什么交给专门的建造者客户端只管拿结果。二、为什么需要建造者模式动机很多初学者会问直接newsetXxx不行吗我们先看两种朴素写法的痛点才能理解 Builder 的价值。2.1 痛点一重叠构造器Telescoping Constructor为了让对象必填字段完整最常见的是写一堆重载构造器// 反模式构造器越叠越长调用方根本记不住参数顺序publicHouse(Stringfloor){...}publicHouse(Stringfloor,Stringwall){...}publicHouse(Stringfloor,Stringwall,Stringhousetop){...}// 调用哪个参数是墙哪个是房顶可读性极差HousehnewHouse(木地板,砖墙,琉璃瓦);参数一多真实项目里 5~10 个很常见调用方极易传错顺序且无法表达只设置房顶、其余默认这种部分赋值。2.2 痛点二JavaBean 的 setXxx 模式退一步用无参构造 setterHousehnewHouse();h.setFloor(木地板);h.setWall(砖墙);h.setHousetop(琉璃瓦);可读性好了但带来两个硬伤对象状态不一致在setFloor和setHousetop之间对象处于半成品状态若此时被别的线程读到会得到不完整的对象。无法做必填校验你没法强制要求房顶必须填少调一个 setter 编译器也不会报错。2.3 Builder 如何解决建造者模式把设置参数和真正创建对象分离所有参数先攒在 Builder 里最后一次性build()出完整、且可设为不可变的对象。既保留了 setXxx 的可读性又规避了半成品状态与顺序混乱。这正是《Effective Java》第 2 条的核心理念。三、建造者模式的结构角色与职责角色职责产品Product最终被构建的复杂对象如House抽象建造者Builder声明创建产品各部件的接口如makeFloor并提供获取产品的方法getHouse具体建造者Concrete Builder实现各部件构建逻辑并组装出具体产品如PingFangBuilder指挥者Director持有 Builder按固定流程调用各步骤隔离构建顺序与客户端客户Client创建具体建造者与指挥者触发构建并获取产品构建流程Client创建ConcreteBuilder→ 交给Director→Director.makeHouse()依次调用makeFloor / makeWall / makeHousetop→Builder.getHouse()返回组装好的House。客户端完全不碰House的setXxx。四、代码实现盖房子示例4.1 产品HousepublicclassHouse{privateStringfloor;// 地板privateStringwall;// 墙privateStringhousetop;// 房顶publicStringgetFloor(){returnfloor;}publicvoidsetFloor(Stringfloor){this.floorfloor;}publicStringgetWall(){returnwall;}publicvoidsetWall(Stringwall){this.wallwall;}publicStringgetHousetop(){returnhousetop;}publicvoidsetHousetop(Stringhousetop){this.housetophousetop;}OverridepublicStringtoString(){returnfloor | wall | housetop;}}4.2 抽象建造者publicinterfaceHouseBuilder{voidmakeFloor();// 修地板voidmakeWall();// 修墙voidmakeHousetop();// 修房顶HousegetHouse();// 取回产品}4.3 具体建造者平房工程队publicclassPingFangBuilderimplementsHouseBuilder{privateHousehousenewHouse();OverridepublicvoidmakeFloor(){house.setFloor(平房---地板);}OverridepublicvoidmakeWall(){house.setWall(平房---墙);}OverridepublicvoidmakeHousetop(){house.setHousetop(平房---房顶);}OverridepublicHousegetHouse(){returnhouse;}}4.4 指挥者DirectorpublicclassHouseDirector{privateHouseBuilderbuilder;publicHouseDirector(HouseBuilderbuilder){this.builderbuilder;}// 按固定流程装配publicvoidmakeHouse(){builder.makeFloor();builder.makeWall();builder.makeHousetop();}}4.5 客户测试类publicclassMainClass{publicstaticvoidmain(String[]args){// 工程队来修HouseBuilderbuildernewPingFangBuilder();// 设计者指挥者来调度HouseDirectordirectornewHouseDirector(builder);director.makeHouse();Househousebuilder.getHouse();System.out.println(house.getFloor());// 平房---地板System.out.println(house);// 平房---地板 | 平房---墙 | 平房---房顶}}关键点客户端只new PingFangBuilder()new HouseDirector(builder)makeHouse()从始至终没调用过House的setXxx构建细节被完全封装。五、应用场景对象创建本身Builder 模式就是为对象的创建而设计的模式。创建的是复合对象被创建对象由多个部件组成复合属性如House含地板/墙/房顶。关注各部件的构建过程不同 Builder 对同一产品属性有不同的构建方法如平房队 vs 别墅队通过切换 Builder 即可产出不同形态的产品。参数多且部分可选当类的构造参数 ≥ 4 个且不少是可选时Builder 比重叠构造器/JavaBean 都优雅见第二节动机。六、优缺点优点封装性好客户端不感知对象内部组装细节。便于控制构建过程Director 可约束先墙后顶等强制顺序。易扩展新增一种产品形态只需新增一个 ConcreteBuilder符合开闭原则。链式写法可读性强且能产出不可变对象见第七节约。缺点类数量变多每多一种 Builder 就多一套类简单对象用它会过度设计。经典写法多了 Director 层代码量上升构建逻辑分散在多个方法里调试不如单构造器直观。七、扩展省略 Director 的链式 Builder现代最常用上面是 GOF 经典写法含 Director。但在实际项目中更常见的是省掉 Director、用内部 Builder 做方法链可读性与安全性都更好典型如OkHttp.Request.Builder、Gson、Lombok Builder、Spring UriComponentsBuilder。⚠️ 常见误区StringBuilder常被拿来类比但它本质是流式 APIFluent API并非严格意义上的 GOF 建造者它没有产品 多个建造步骤 Director的完整结构。初学类比可以正式文章里别把它当标准范例。publicclassHouse{// 字段 final 无 setter → 构建出的对象不可变天然线程安全privatefinalStringfloor;privatefinalStringwall;privatefinalStringhousetop;// 私有构造强制走 Builder外部无法直接 newprivateHouse(Builderb){this.floorb.floor;this.wallb.wall;this.housetopb.housetop;}// 静态内部 BuilderpublicstaticclassBuilder{privateStringfloor;privateStringwall;privateStringhousetop;publicBuilderfloor(Stringfloor){this.floorfloor;returnthis;}publicBuilderwall(Stringwall){this.wallwall;returnthis;}publicBuilderhousetop(Stringhousetop){this.housetophousetop;returnthis;}// 最后一步统一创建可在此做必填校验publicHousebuild(){// 例强制房顶必填// if (housetop null) throw new IllegalStateException(房顶必填);returnnewHouse(this);}}OverridepublicStringtoString(){returnfloor | wall | housetop;}}// 调用链式一步到位顺序无所谓可读性高HousehousenewHouse.Builder().floor(木地板).wall(砖墙).housetop(琉璃瓦).build();System.out.println(house);这种写法把构建步骤变成链式调用既隐藏细节又省去 Director 类还能在build()里做必填校验、产出不可变对象是日常开发的首选。经典写法胜在构建流程由 Director 统一管控适合构建顺序复杂、需要被严格约束的场景如必须按依赖先后装配的复杂系统。八、与工厂模式的区别对比维度工厂模式建造者模式关注点创建哪一种产品复杂产品的构建步骤与顺序产品复杂度多为单一/简单对象多为复合对象多部件创建方式一步getXxx()直接拿到分步调用各部件方法后组装典型结构工厂 产品Builder Director Product举例FruitFactory.getApple()HouseBuilder逐步盖房记忆工厂回答造哪个建造者回答怎么一步步造出来。九、总结建造者模式 把复合对象的构建过程抽象成指挥者调度 建造者组装客户端只拿成品。五大角色产品、抽象建造者、具体建造者、指挥者、客户。它解决的是参数多、构造复杂的对象创建问题优于重叠构造器与 JavaBean 的 setXxx。现代项目优先用链式 Builder免 Director、字段 final 不可变构建顺序复杂且需强约束时再用经典 Director 写法。选型口诀单一产品用工厂复合对象分步装用建造者。一句话总结当你的对象是由一堆零件拼起来的复杂货别让客户端满屏setXxx——交给建造者分步装好再交付。
返回列表