了解更多开源请访问:开源基础软件社区https://ost.51cto.com1.生活case沙哥:“哥,这车是怎么造的?”大哥:“汽车从外面看,有车身、座椅、轮胎,从里面看,有发动机、方向盘、电气系统、制动系统、冷却系统等,这些复杂零部件一般不是由一家厂家完成,而是交付给汽车零部件厂家,不同的厂家最终会完成不同零部件的生产,采购完整的零部件,最后在车间完成整个组装。汽车这个复杂的物体可以使用建造者模式将零部件和组装过程分开,帮助我们快速完成汽车的建造。二、建造者模式建造者模式帮助我们建造复杂的对象,而不用直接实例化它们的结构,或者编写它们需要的逻辑。想象一下,一个对象可能有几十个字段,这些字段本身都是比较复杂的结构。现在,您有许多具有这些特征的对象,而且还可以有更多。我们不想在包中编写创建所有这些对象的逻辑,而只是在我们需要使用它们的地方编写。一、Go中的对象实例在Go语言中,实例的创建可以很简单,比如简单的提供{},然后让实例的值为零;它也可能非常复杂,比如一个对象需要执行一些API调用,检查状态,并为其字段创建一个对象。你也可以拥有一个由多个对象组成的对象,这在Go中很常见,因为Go不支持继承。此外,您可以使用相同的技术来创建多种类型的对象。例如,您将使用几乎相同的技术制造汽车和公共汽车,只是尺寸和座位数量不同,那么我们为什么不重用构建过程呢?这就是建造者模式的用武之地。2.建造者模式的优点抽象复杂的创建,将对象和对象使用者分开通过填写字段和创建嵌入对象来逐步创建对象可以在多个对象之间重用对象创建方法3.示例车辆制造的建造者模式通常被描述为一个主管、几个建造者和他们创造的产品之间的关系。我们看汽车的例子,创建一个汽车制造商Builder,制造一辆汽车(Product)的过程可能会有一些差异,但是对于每一辆车,整体的过程可以概括为以下几个步骤:选择车辆类型组装如果你仔细想想,你可以重复这个步骤来描述自行车和摩托车的构造。在以下示例中,主管的角色由Manufacturing变量表示。4.设计思路如上所述,我们要处理一些Builder变量和一个独立董事。主管负责领导实际建造者建造产品的过程。因此,对整车制造商的要求是:需要有一个制造对象来制造整车的一切。当使用汽车构建器构建器时,返回的车辆产品必须有4个轮子、5个座位??和一个结构使用摩托车构建器时,返回的车辆产品必须有2个轮子、2个座位和定义为摩托车的结构。任何BuilderProcess构建器都必须打开车辆产品的修改。功能结构图如下:3.测试驱动开发基于前面的设计流程,我们将构建一个director变种:ManufacturingDirector,用于汽摩产品构建者的构建过程。dierctor是负责人,builder是实际的builder。1.Builder接口声明Builder声明如下:packagecreationaltypeBuildProcessinterface{SetWheels()BuildProcessSetSeats()BuildProcessSetStructure()BuildProcessGetVehicle()VehicleProduct}BuildProcess接口定义了构建车辆所需的步骤。因此,每辆车的Builder都必须实现这个接口。在每个SetXXX()函数中,返回每个构造过程,然后连接步骤返回一个GetVehicle()方法。2.Directorsupervisor接口ManufacturingDirectorsupervisor接口可以接收不同的Builder:然后有一个Construct()方法,使用Builder重复构建过程,后面会实现这个方法。SetBuilder()方法用于替换不同的Builder。//生产主管类型ManufacturingDirectorstruct{}func(f*ManufacturingDirector)Construct(){//构建过程}func(f*ManufacturingDirector)SetBuilder(bBuildProcess){//选择builder}3.产品产品结构product是最终的我们正在制作的对象。在上面的简单示例中,我们假设车辆由车轮、座椅和结构组成。//ProducttypeVehicleProductstruct{WheelsintSeatsintStructurestring}4.Builder第一个Builder是CarBuilderBuilder,它需要我们实现BuildProcess接口中定义的方法://CarBuildertypeCarBuilderstruct{}func(c*CarBuilder)SetWheels()BuildProcess{返回nil}func(c*CarBuilder)SetSeats()BuildProcess{返回nil}func(c*CarBuilder)SetStructure()BuildProcess{返回nil}func(c*CarBuilder)GetVehicle()VehicleProduct{returnVehicleProduct{}}同样,摩托车制造商如下://MotorcyclebuildertypeMotorBuilderstruct{}func(m*MotorBuilder)SetWheels()BuildProcess{returnnil}func(m*MotorBuilder)SetSeats()BuildProcess{returnnil}func(m*MotorBuilder)SetStructure()BuildProcess{returnnil}func(m*MotorBuilder)GetVehicle()VehicleProduct{returnVehicleProduct{}}最后得到完整的creational.go文件:packagecreational//构建过程类型BuildProcess接口{SetWheels()BuildProcessSetSeats()BuildProcessSetStructure()BuildProcessGetVehicle()VehicleProduct}//生产主管类型ManufacturergDirectorstruct{}func(f*ManufacturingDirector)Construct(){//等待实现}func(f*ManufacturingDirector)SetBuilder(bBuildProcess){//等待实现}//产品类型VehicleProductstruct{WheelsintSeatsintStructurestring}//汽车制造商类型CarBuilderstruct{}func(c*CarBuilder)SetWheels()BuildProcess{returnnil}func(c*CarBuilder)SetSeats()BuildProcess{returnnil}func(c*CarBuilder)SetStructure()BuildProcess{returnnil}func(c*CarBuilder)GetVehicle()VehicleProduct{returnVehicleProduct{}}//摩托车制造商类型MotorBuilderstruct{}func(m*MotorBuilder)SetWheels()BuildProcess{returnnil}func(m*MotorBuilder)SetSeats()BuildProcess{returnnil}func(m*MotorBuilder)SetStructure()BuildProcess{returnnil}func(m*MotorBuilder)GetVehicle()VehicleProduct{returnVehicleProduct{}}5.编写测试用例对于上面写的构造过程,我们可以执行以下测试并创建一个creational_test.go同一目录下的文件1、首先测试小车的制作过程。假设最终生产的汽车有4个轮子,5个座位,结构为Cartype。编写如下代码:)ifcar.Wheels!=4{t.Errorf("汽车上的轮子必须是4,它们是%d\n",car.Wheels)}ifcar.Structure!="Car"{t.Errorf("汽车上的结构必须是'Car'并且是%s\n",car.Structure)}ifcar.Seats!=5{t.Errorf("汽车上的座位必须是5并且它们是%d\n",car.Seats)}}我们编写了3个简单的测试来检查汽车是否为制造型。运行单元测试,结果如下:$gotest-v.===RUNTestBuilderPatterncreative_test.go:16:Wheelsonacarmustbe4andtheywere0creative_test.go:20:Structureonacarmustbe'Car'andwascreative_test.go:24:Seatsonacarmustbe5andtheywere0---FAIL:TestBuilderPattern(0.00s)FAILFAILgithub.com/yuzhoustayhungry/GoDesignPattern/creational0.860sFAIL如上所示,所有3个测试单元都显示Failed,那我们就来看看怎么写摩托车的单元测试吧。2、摩托车motorCycle单元测试如下:motorBuilder:=&MotorBuilder{}manufacturingComplex.SetBuilder(motorBuilder)manufacturingComplex.Construct()motorCycle:=motorBuilder.GetVehicle()ifmotorCycle.Wheels!=2{t.Errorf("摩托车上的轮子必须是2,它们是%d\n",motorCycle.Wheels)}ifmotorCycle.Structure!="MotorCycle"{t.Errorf("摩托车上的结构必须是'MotorCycle'并且是%s\n",motorCycle.Structure)}ifmotorCycle.Seats!=2{t.Errorf("SeatsonamotorCyclemustbe2andwas%d\n",motorCycle.Seats)}构造过程类似于汽车,我们只需要在manufacturingComplex中添加.SetBuilder(motorBuilder)传给motorBuilder即可。我们假设摩托车有2个轮子和2个座位,结构一定是MotorCyle。运行测试代码,得到如下结果:$gotest-v.===RUNTestBuilderPatterncreative_test.go:16:Wheelsonacarmustbe4andtheywere0creative_test.go:20:Structureonacarmustbe'Car'并且是creative_test.go:24:汽车上的座位必须是5而它们是0creative_test.go:36:摩托车上的轮子必须是2而它们是0creative_test.go:41:摩托车上的结构必须是'MotorCycle'并且是creative_test.go:46:motorCycle上的座位必须是2并且是0---失败:TestBuilderPattern(0.00s)FAILFAILgithub.com/yuzhoustayhungry/GoDesignPattern/creational0.595sFAIL如您所见,单元测试也失败了,因为我们还没有完成具体构建器模式的实现。下一步是实际的实施过程。4.Builder模式Go实现为了实现builder,你肯定已经开始有点自己的想法了。让我们实现我们之前创建的creation.go文件。新建creation.go文件的代码如下:packagecreational//构建过程typeBuildProcessinterface{SetWheels()BuildProcessSetSeats()BuildProcessSetStructure()BuildProcessGetVehicle()VehicleProduct}//productionsupervisortypeManufacturingDirectorstruct{builderBuildProcess}func(f*ManufacturingDirector)Construct(){//f.builder.SetSeats().SetStructure().SetWheels()}func(f*ManufacturingDirector)SetBuilder(bBuildProcess){//f.builder=b}//产品类型VehicleProductstruct{WheelsintSeatsintStructurestring}//汽车制造商类型CarBuilderstruct{vVehicleProduct}func(c*CarBuilder)SetWheels()BuildProcess{//returnnilc.v.Wheels=4returnc}func(c*CarBuilder)SetSeats()BuildProcess{//返回nilc.v.Seats=5returnc}func(c*CarBuilder)SetStructure()BuildProcess{//返回nilc.v.Structure="Car"returnc}func(c*CarBuilder)GetVehicle()VehicleProduct{//返回VehicleProduct{}返回c.v}//摩托汽车制造商typeMotorBuilderstruct{vVehicleProduct}func(m*MotorBuilder)SetWheels()BuildProcess{//returnnilm.v.Wheels=2returnm}func(m*MotorBuilder)SetSeats()BuildProcess{//returnnilm.v.Seats=2returnm}func(m*MotorBuilder)SetStructure()BuildProcess{//returnnilm.v.Structure="MotorCycle"returnm}func(m*MotorBuilder)GetVehicle()VehicleProduct{//returnVehicleProduct{}returnm.v}修改后creational_test.go文件如下:packagecreationalimport"testing"funcTestBuilderPattern(t*testing.T){manufacturingComplex:=ManufacturingDirector{}carBuilder:=&CarBuilder{}manufacturingComplex.SetBuilder(carBuilder)manufacturingComplex.Construct()car:=carBuilder.GetVehicle()ifcar.Wheels!=4{t.Errorf("汽车上的轮子必须是4,它们是%d\n",car.Wheels)}ifcar.Structure!="Car"{t.Errorf("汽车上的结构必须是'汽车'并且是%s\n",car.Structure)}ifcar.Seats!=5{t.Errorf("汽车上的座位必须是5,它们是%d\n",car.Seats)}motorBuilder:=&MotorBuilder{}manufacturingComplex.SetBuilder(motorBuilder)manufacturingComplex.Construct()motorCycle:=motorBuilder.GetVehicle()ifmotorCycle.Wheels!=2{t.Errorf("摩托车上的轮子必须是2,它们是%d\n",motorCycle.Wheels)}ifmotorCycle.Structure!="MotorCycle"{t.Errorf("摩托车上的结构必须是'MotorCycle'并且是%s\n",motorCycle.Structure)}ifmotorCycle.Seats!=2{t.Errorf("摩托车上的座椅必须是2并且是%d\n",motorCycle.Seats)}}执行所有方法后,再次运行gotest-v。执行后的测试结果:$gotest-v.===RUNTestBuilderPattern---PASS:TestBuilderPattern(0.00s)PASSokgithub.com/yuzhoustayhungry/GoDesignPattern/creational0.255s恭喜,至此,测试用例全部通过.也可以看出builder模式是一种可重复的模式,但是在BuildProcess接口的每个方法中,我们可以封装尽可能多的复杂对象,这样用户就不会真正了解对象创建的细节。5.建造者模式总结就像造汽车一样,建造者模式的核心是如何一步步建造一个包含多个组件的完整对象,并使用相同的建造过程建造不同的产品。在软件开发过程中,如果需要创建复杂的对象,希望系统具有良好的灵活性和可扩展性,可以考虑使用构建器模式。了解更多开源知识,请访问:开源基础软件社区https://ost.51cto.com。
