生成器模式 是一种创建型设计模式, 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。
使用建造者模式,用户就只需要指定需要建造的类型,具体的建造过程和细节并不需要知道。
建造者模式允许修改一个产品的内部表示。
它将构造和表示两块代码隔离开来。
它很好的控制了构建过程。
建造者模式流程说明:
创建大型产品建造者
客户端创建指挥者 Director
对象并配置它所需要的建造者 Builder
对象
Director
负责通知 builder
何时建造 product
的部件
Builder
处理 director
的请求并添加 product
的部件
客户端从 builder
处获得 product
问题 假设有这样一个复杂对象, 在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。 这些初始化代码通常深藏于一个包含众多参数且让人基本看不懂的构造函数中; 甚至还有更糟糕的情况, 那就是这些代码散落在客户端代码的多个位置。
例如, 我们来思考如何创建一个房屋House
对象。 建造一栋简单的房屋, 首先你需要建造四面墙和地板, 安装房门和一套窗户, 然后再建造一个屋顶。 但是如果你想要一栋更宽敞更明亮的房屋, 还要有院子和其他设施 (例如暖气、 排水和供电设备), 那又该怎么办呢?
最简单的方法是扩展房屋基类, 然后创建一系列涵盖所有参数组合的子类。 但最终你将面对相当数量的子类。 任何新增的参数 (例如门廊类型) 都会让这个层次结构更加复杂。
另一种方法则无需生成子类。 你可以在房屋基类中创建一个包括所有可能参数的超级构造函数, 并用它来控制房屋对象。 这种方法确实可以避免生成子类, 但它却会造成另外一个问题。
通常情况下, 绝大部分的参数都没有使用, 这使得对于构造函数的调用十分不简洁。 例如, 只有很少的房子有游泳池, 因此与游泳池相关的参数十之八九是毫无用处的。
解决方案 生成器模式建议将对象构造代码从产品类中抽取出来, 并将其放在一个名为生成器 的独立对象中。
该模式会将对象构造过程划分为一组步骤, 比如 buildWalls
创建墙壁和 buildDoor
创建房门创建房门等。 每次创建对象时, 你都需要通过生成器对象执行一系列步骤。 重点在于你无需调用所有步骤, 而只需调用创建特定对象配置所需的那些步骤即可。
当你需要创建不同形式的产品时, 其中的一些构造步骤可能需要不同的实现。 例如, 木屋的房门可能需要使用木头制造, 而城堡的房门则必须使用石头制造。
在这种情况下, 你可以创建多个不同的生成器, 用不同方式实现一组相同的创建步骤。 然后你就可以在创建过程中使用这些生成器 (例如按顺序调用多个构造步骤) 来生成不同类型的对象。
例如, 假设第一个建造者使用木头和玻璃制造房屋, 第二个建造者使用石头和钢铁, 而第三个建造者使用黄金和钻石。 在调用同一组步骤后, 第一个建造者会给你一栋普通房屋, 第二个会给你一座小城堡, 而第三个则会给你一座宫殿。 但是, 只有在调用构造步骤的客户端代码可以通过通用接口与建造者进行交互时, 这样的调用才能返回需要的房屋。
指挥者 你可以进一步将用于创建产品的一系列生成器步骤调用抽取成为单独的指挥类。 指挥类可定义创建步骤的执行顺序, 而生成器则提供这些步骤的实现。
严格来说, 你的程序中并不一定需要指挥类。 客户端代码可直接以特定顺序调用创建步骤。 不过, 指挥类中非常适合放入各种例行构造流程, 以便在程序中反复使用。
此外, 对于客户端代码来说, 指挥类完全隐藏了产品构造细节。 客户端只需要将一个生成器与主管类关联, 然后使用主管类来构造产品, 就能从生成器处获得构造结果了。
生成器模式结构
建造者 (Builder) 接口声明在所有类型建造者中通用的产品构造步骤。
具体建造者 (Concrete Builders) 提供构造过程的不同实现。 具体建造者也可以构造不遵循通用接口的产品。
产品 (Products) 是最终生成的对象。 由不同建造者构造的产品无需属于同一类层次结构或接口。
主管 (Director) 类定义调用构造步骤的顺序, 这样你就可以创建和复用特定的产品配置。
客户端 (Client) 必须将某个建造者对象与主管类关联。 一般情况下, 你只需通过主管类构造函数的参数进行一次性关联即可。 此后主管类就能使用建造者对象完成后续所有的构造任务。 但在客户端将建造者对象传递给主管类制造方法时还有另一种方式。 在这种情况下, 你在使用主管类生产产品时每次都可以使用不同的建造者。
代码示例: 下面关于生成器 模式的例子演示了你可以如何复用相同的对象构造代码来生成不同类型的产品——例如汽车 (Car)。
汽车是一个复杂对象, 有数百种不同的制造方法。 我们没有在 汽车
类中塞入一个巨型构造函数, 而是将汽车组装代码抽取到单独的汽车生成器类中。 该类中有一组方法可用来配置汽车的各种部件。
如果客户端代码需要组装一辆与众不同、 精心调教的汽车, 它可以直接调用生成器。 或者, 客户端可以将组装工作委托给主管类, 因为主管类知道如何使用生成器制造最受欢迎的几种型号汽车。
比如说我是个老司机,但是除了开车还想造车,但是车的构造实在是太复杂了,那么我们就可以将车拆分…
4个轮子、1个底盘、1个发动机,1个驾驶位
好了,为了简便,就造这四个吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public class Car { private CarType carType; private WheelType wheels; private ChassisType chassis; private EngineType engine; private int seats; public Car () { } public Car (CarType carType, WheelType wheels, ChassisType chassis, EngineType engine, int seat) { this .carType = carType; this .wheels = wheels; this .chassis = chassis; this .engine = engine; this .seats = seat; } public CarType getCarType () { return carType; } public WheelType getWheels () { return wheels; } public ChassisType getChassis () { return chassis; } public EngineType getEngine () { return engine; } public int getSeat () { return seats; } @Override public String toString () { return "Car{" + "carType=" + carType + ", wheels=" + wheels + ", chassis=" + chassis + ", engine=" + engine + ", seats=" + seats + '}' ; } }
我们有汽车零部件的各种枚举类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public enum CarType { CITY_CAR, SPORT_CAR, SUV } public enum WheelType { CITY_CAR_WHEEL, SPORT_CAR_WHEEL, SUV_CAR_WHEEL } public enum ChassisType { CITY_CAR_CHASSIS, SPORT_CAR_CHASSIS, SUV_CAR_CHASSIS } public enum EngineType { CITY_CAR_ENGINE, SPORT_CAR_ENGINE, SUV_CAR_ENGINE }
然后我需要一些具体的汽车构造者,这里我们有三种具体的汽车构建者:城市轿车构建者CityCarBuilder
、运动跑车构建者SportCarBuilder
、SUV汽车构建者SUVCarBuilder
。
CityCarBuilder.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.design.patterns.builder;public class CityCarBuilder implements Builder { private CarType carType; private WheelType wheels; private ChassisType chassis; private EngineType engine; private int seat; @Override public void createCar () { this .carType = CarType.CITY_CAR; } @Override public void buildWheels () { this .wheels = WheelType.CITY_CAR_WHEEL; } @Override public void buildChassis () { this .chassis = ChassisType.CITY_CAR_CHASSIS; } @Override public void buildEngine () { this .engine = EngineType.CITY_CAR_ENGINE; } @Override public void buildSeat () { this .seat = 5 ; } @Override public Car getCar () { return new Car(carType, wheels, chassis, engine, seat); } }
SportCarBuilder.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.design.patterns.builder;public class SportCarBuilder implements Builder { private CarType carType; private WheelType wheels; private ChassisType chassis; private EngineType engine; private int seat; @Override public void createCar () { this .carType = CarType.SPORT_CAR; } @Override public void buildWheels () { this .wheels = WheelType.SPORT_CAR_WHEEL; } @Override public void buildChassis () { this .chassis = ChassisType.SPORT_CAR_CHASSIS; } @Override public void buildEngine () { this .engine = EngineType.SPORT_CAR_ENGINE; } @Override public void buildSeat () { this .seat = 2 ; } @Override public Car getCar () { return new Car(carType, wheels, chassis, engine, seat); } }
SUVCarBuilder.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.design.patterns.builder;public class SUVCarBuilder implements Builder { private CarType carType; private WheelType wheels; private ChassisType chassis; private EngineType engine; private int seat; @Override public void createCar () { this .carType = CarType.SUV; } @Override public void buildWheels () { this .wheels = WheelType.SUV_CAR_WHEEL; } @Override public void buildChassis () { this .chassis = ChassisType.SUV_CAR_CHASSIS; } @Override public void buildEngine () { this .engine = EngineType.SUV_CAR_ENGINE; } @Override public void buildSeat () { this .seat = 7 ; } @Override public Car getCar () { return new Car(carType, wheels, chassis, engine, seat); } }
这些建造者都实现了Builder
接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.design.patterns.builder;public interface Builder { void createCar () ; void buildWheels () ; void buildChassis () ; void buildEngine () ; void buildSeat () ; Car getCar () ; }
最后我们需要一个Director
类负责按照特定顺序执行生成步骤。其在根据特定步骤或配置来生成产品时 会很有帮助。由于客户端可以直接控制生成器,我们可以调用Director
类的setBuilder
来切换产品的具体建造者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.design.patterns.builder;public class Director { private Builder builder; public Car constructCar () { builder.createCar(); builder.buildWheels(); builder.buildChassis(); builder.buildEngine(); builder.buildSeat(); return builder.getCar(); } public Director (Builder builder) { this .builder = builder; } public void setBuilder (Builder builder) { this .builder = builder; } public Builder getBuilder () { return builder; } }
现在指挥者和建造者都已经准备好了,可以进行建造了,调用指挥者的constructCar()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.design.patterns.builder;public class Main { public static void main (String[] args) { CityCarBuilder cityCarBuilder = new CityCarBuilder(); Director director = new Director(cityCarBuilder); Car cityCar = director.constructCar(); System.out.println(cityCar.toString()); SportCarBuilder sportCarBuilder = new SportCarBuilder(); director.setBuilder(sportCarBuilder); Car sportCar = director.constructCar(); System.out.println(sportCar.toString()); SUVCarBuilder suvCarBuilder = new SUVCarBuilder(); director.setBuilder(suvCarBuilder); Car suv = director.constructCar(); System.out.println(suv.toString()); } }
程序输出:
1 2 3 Car{carType=CITY_CAR, wheels=CITY_CAR_WHEEL, chassis=CITY_CAR_CHASSIS, engine=CITY_CAR_ENGINE, seats=5} Car{carType=SPORT_CAR, wheels=SPORT_CAR_WHEEL, chassis=SPORT_CAR_CHASSIS, engine=SPORT_CAR_ENGINE, seats=2} Car{carType=SUV, wheels=SUV_CAR_WHEEL, chassis=SUV_CAR_CHASSIS, engine=SUV_CAR_ENGINE, seats=7}
生成器模式适合应用场景: 使用生成器模式可避免 “重叠构造函数 (telescoping constructor)” 的出现。
假设你的构造函数中有十个可选参数, 那么调用该函数会非常不方便; 因此, 你需要重载这个构造函数, 新建几个只有较少参数的简化版。 但这些构造函数仍需调用主构造函数, 传递一些默认数值来替代省略掉的参数。
1 2 3 4 5 class Pizza { Pizza(int size) { …… } Pizza(int size, boolean cheese) { …… } Pizza(int size, boolean cheese, boolean pepperoni) { …… } // ……
只有在 C# 或 Java 等支持方法重载的编程语言中才能写出如此复杂的构造函数。
生成器模式让你可以分步骤生成对象, 而且允许你仅使用必须的步骤。 应用该模式后, 你再也不需要将几十个参数塞进构造函数里了。
当你希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用生成器模式。
如果你需要创建的各种形式的产品, 它们的制造过程相似且仅有细节上的差异, 此时可使用生成器模式。
基本生成器接口中定义了所有可能的制造步骤, 具体生成器将实现这些步骤来制造特定形式的产品。 同时, 主管类将负责管理制造步骤的顺序。
已知使用:
java.lang.StringBuilder
java.nio.ByteBuffer
as well as similar buffers such as FloatBuffer, IntBuffer and so on.
java.lang.StringBuffer
All implementations of java.lang.Appendable
Apache Camel builders
Apache Commons Option.Builder