文章目录
- 简介
- 场景
- 解决
- Product类代表复杂对象
- Builder接口规范可配置步骤
- ConcreateBuilder提供差异化实现
- Director统一编排构造流程
- Client使用示例
- 总结
简介
Builder是一种创建型设计模式,可让你逐步构建复杂对象。这个模式允许你使用相同的构造代码生成对象的不同类型和表示形式?怎么理解?。
场景
假设一个复杂的对象,它需要逐步初始化许多字段和嵌套对象。通常它的初始化代码会放在带有大量参数的构造函数中,甚至可能散布在整个客户端代码中。
如上图,假设你需要创建一个House对象。对于一栋简单的房子,你只需要建造四面墙和一层地板,安装一扇门,安装一对窗户,并建造屋顶。但是,如果你想要一栋更豪华的房子,有后院和其他设施(如供暖系统、管道和电线),怎么办?
最简单的解决方案是扩展基House类并创建一组子类来覆盖所有参数组合。这样将得到相当多的子类。任何新参数(例如门廊样式)都需要进一步扩大类层次结构。
public class House {// 基础参数protected int wallCount;protected boolean hasDoor;// 扩展参数(部分子类不使用)protected boolean hasSwimmingPool; protected boolean hasGarage;protected boolean hasGarden;// 分层构造函数(引发参数冗余)public House(int wallCount, boolean hasDoor) {this.wallCount = wallCount;this.hasDoor = hasDoor;}// 参数爆炸式重载(违反单一职责原则)public House(int wallCount, boolean hasDoor, boolean hasSwimmingPool) {this(wallCount, hasDoor);this.hasSwimmingPool = hasSwimmingPool;}public House(int wallCount, boolean hasDoor, boolean hasSwimmingPool, boolean hasGarage) {this(wallCount, hasDoor, hasSwimmingPool);this.hasGarage = hasGarage;}// 更多构造函数组合...
}// 基础房屋(必须调用父类构造传递无效参数)
class BasicHouse extends House {public BasicHouse() {super(4, true, false, false); // 强制填写无用参数}
}// 带泳池房屋
class HouseWithSwimmingPool extends House {public HouseWithSwimmingPool() {super(4, true, true, false); }
}// 带车库房屋
class HouseWithGarage extends House {public HouseWithGarage() {super(4, true, false, true); }
}// 混合组合场景需创建新子类(类数量激增)
class LuxuryHouse extends House {public LuxuryHouse() {super(6, true, true, true, true); // 需要修改父类构造函数签名}
}public class Client {public static void main(String[] args) {// 类型强制转换问题House basic = new BasicHouse(); House luxury = new LuxuryHouse();// 新增类型无法动态组合(需预定义所有子类)if(needHeatedPool) {// 必须创建新的HeatedPoolHouse子类}}
}
或者可以在基House类中创建一个巨大的构造函数,其中包含创建房屋对象的所有可能参数。
public class House {// 核心参数private int wallCount;private int doorCount;// 可选设施private boolean hasSwimmingPool; // 90%场景无用参数private boolean hasSolarPanels;private boolean hasUndergroundBunker;// 装饰参数private String roofMaterial;private String wallPaintColor;private boolean hasLandscaping;// 电器系统 private boolean hasSmartHomeSystem;private boolean hasCentralAir;/*** 万能的构造函数(包含18个参数)* @param wallCount 墙体数量(必须)* @param doorCount 门数量(必须)* @param hasSwimmingPool 泳池存在性(90%为false)* @param solarPanels 太阳能板(特殊设施)* ...其他参数省略...*/public House(int wallCount, int doorCount,boolean hasSwimmingPool,boolean hasSolarPanels,boolean hasUndergroundBunker,String roofMaterial,String wallPaintColor,boolean hasLandscaping,boolean hasSmartHomeSystem,boolean hasCentralAir// ...还有8个额外参数...) {this.wallCount = wallCount;this.doorCount = doorCount;this.hasSwimmingPool = hasSwimmingPool; // 参数浪费this.hasSolarPanels = hasSolarPanels;// ...剩余参数初始化逻辑...}
}// 客户端被迫填写无用参数
public class Client {public static void main(String[] args) {// 基础住宅实例化(需要填写7个默认false)House basicHouse = new House(4, 1, false, false, false, // 泳池/太阳能/地堡"Asphalt", "White", false, false, false);// 豪华住宅调用(所有参数必须显式指定)[^3]House mansion = new House(12, 6, true, true, true,"Slate", "Ivory", true,true, true// ...仍需补全所有参数...);}
}
虽然这种方法消除了对子类的需求,但它也会带来另一个问题。看一下新增参数时的情况。
// 新增"有无宠物屋"参数将导致灾难
public House(//...原有18个参数...boolean hasPetHouse // 新增参数需要修改所有构造调用
) { /*...*/ }// 所有现有调用都需要相应调整(即使不需要宠物屋)
House basic = new House(..., false /* 被迫添加新参数默认值 */);
解决
Product类代表复杂对象
class House {private String wallMaterial;private int doors;private int windows;private String roofType;private boolean hasSwimmingPool;public void setWallMaterial(String material) { /*...*/ } // 墙体材料设置[^4]public void setDoors(int count) { /*...*/ }public void setWindows(int count) { /*...*/ }public void setRoofType(String type) { /*...*/ }public void setSwimmingPool(boolean has) { /*...*/ } // 可选游泳池配置[^6]
}
Builder接口规范可配置步骤
// Builder接口定义建造步骤
interface HouseBuilder {void reset();void buildWalls();void buildDoors(int count); // 参数化建造方法void buildWindows(int count);void buildRoof(String type);void buildSwimmingPool();House getResult();
}
ConcreateBuilder提供差异化实现
// 石头房屋建造者
class StoneHouseBuilder implements HouseBuilder {private House house = new House();@Overridepublic void reset() { this.house = new House(); // 重置构建状态}@Overridepublic void buildWalls() { // 实现具体建造逻辑house.setWallMaterial("花岗岩");}@Overridepublic void buildRoof(String type) { // 参数化方法示例house.setRoofType(type + "石质屋顶");}@Overridepublic void buildSwimmingPool() { // 实现可选配置house.setSwimmingPool(true);}// 其他方法实现...
}// 木屋建造者
class WoodHouseBuilder implements HouseBuilder { /* 类似实现 */ }// 可扩展第三类建造者(例如现代玻璃房屋)
class ModernHouseBuilder implements HouseBuilder {@Overridepublic void buildWalls() {currentHouse.setWallMaterial("钢化玻璃");}@Overridepublic void buildRoof(String type) {currentHouse.setRoofType("透明生态屋顶");}
}
Director统一编排构造流程
// Director控制建造流程
class ConstructionDirector { // 封装常见建造方案public void constructFamilyHouse(HouseBuilder builder) {builder.reset();builder.buildWalls();builder.buildDoors(3); // 标准流程步骤builder.buildWindows(6);builder.buildRoof("斜顶");}public void constructLuxuryVilla(HouseBuilder builder) {builder.reset();builder.buildWalls();builder.buildDoors(5);builder.buildWindows(10);builder.buildRoof("平顶");builder.buildSwimmingPool(); // 添加可选设施}
}
Client使用示例
public class ConstructionClient {public static void main(String[] args) {ConstructionDirector director = new ConstructionDirector();// 建造石头别墅 HouseBuilder stoneBuilder = new StoneHouseBuilder();director.constructLuxuryVilla(stoneBuilder); // 调用导演建造逻辑House stoneVilla = stoneBuilder.getResult(); // 获取产品// 建造木屋HouseBuilder woodBuilder = new WoodHouseBuilder();woodBuilder.buildSwimmingPool(); // 客户端自定义步骤director.constructFamilyHouse(woodBuilder); House woodHouse = woodBuilder.getResult();}
}
总结
- 生成器(Builder)接口:声明 所有生成器中通用的产品构造步骤。
- 具体生成器(Concrete Builders):提供构造过程的不同实现。具体生成器也可以构造不遵循通用接口的产品。
- 产品(Products)是最终生成的对象。由不同生成器构造的产品不需要属于同一类层次结构或接口。
- (Director)类定义了构造步骤的调用顺序,这样你就可以复用特定的产品创建过程。
- 客户端(Client)必须把某个生成器与Director类关联。一般情况下,你只需通过Director类构造函数的参数进行一次性关联就可以了。之后Direct类就能使用Builder对象完成后续所有的构造任务。或者是在客户端使用Director类构建产品时每次都传入不同的生成器, 这也是我们代码中使用的方式。