首页 > 其他分享 >一起学习 Go 语言设计模式之建造者模式

一起学习 Go 语言设计模式之建造者模式

时间:2022-10-21 21:00:33浏览次数:73  
标签:func cottagesBuilder House 建造 doorType windowType Go 设计模式

一起学习 Go 语言设计模式之建造者模式_建造者

前言

你去买车,你不会只买一个轮胎、一个发动机、一个方向盘,你买的是一辆包括轮胎、方向盘、发动机、底盘、电气系统和车身等多个部件组成的完整骑车。

在设计模式中,建造者模式就是解决如何将这些部件组装成一辆完整的汽车并返回给用户的设计模式。建造者模式为客户端返回的不是一个简单的产品,而是一个由多个部件组成的复杂产品。

思考如下问题

假设让我们思考如何创建一个房屋对象。建造一栋简单的房屋,你需要建造地板和四面的强,安装房门和窗户,然后再建造一个漂亮的屋顶。

但如果想要一个更宽敞舒适的别墅,还需要有院子、游泳池、植物和其他设施(例如中央空调、排水、供电设备),那又该怎么办呢?

一起学习 Go 语言设计模式之建造者模式_设计模式_02

最简单的方法就是扩展房屋的积累,然后创建一系列涵盖所有参数组合的子类。随着房子越复杂,子类越多,任何新增的参数都会让这个层次结构更加复杂。

一起学习 Go 语言设计模式之建造者模式_建造者_03

另外一种方式则无需生成子类,我们可以在房屋基类中创建一个包含所有可能参数的超级构造函数,并用它来控制房屋对象,这种方法确实可以避免生成子类,但它却会造成另外一个问题——构造函数参数太多。

House(windows, doors, rooms, hasSwimPool, hasGarden,...)

但并不是所有的房子都需要游泳池,导致绝大部分的参数都没有使用,使得构造函数的声明复杂,调用不整洁

解决方式就是今天要介绍的建造者模式。

建造者模式概念

建造者模式(Builder Pattern),又称生成器模式,是较为复杂的创建者模式,它将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无需知道复杂对象的内部组成部分与装配方式,只需要知道所需的建造者类型即可。

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

结构图如下:

一起学习 Go 语言设计模式之建造者模式_建造者模式_04

由上图可以知道,建造者模式包含 4 个角色:

  • ​Builder​​ 抽象建造者:它为创建一个产品 Product 对象的各个部件指定抽象接口,这个接口一般包括两类方法:
  • ​buildPartX()​​ :用于创建复杂对象的各个部件
  • ​getResult()​​ :用于返回复杂对象
  • ​ConcreteBuilder​​ 具体建造者:它实现了​​Builder​​ 接口,实现各个部件的具体构造和装配方法,定义并明确其所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象
  • ​Product​​ 产品角色:它是最终被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义其装配过程。
  • ​Director​​ 主管、指挥者:指挥者又被称为导演类,定义调用构造步骤的顺序。它负责安排负责对象的建造次序,指挥者和抽象建造者之间存在关联关系,可以在其​​construct()​​ 建造方法中调用建造者对象的部件构造与装配方法,完成负责对象的建造。

Go 代码示例

代码组织结构如下:

一起学习 Go 语言设计模式之建造者模式_设计模式_05

  1. 首先创建​​house.go​​ 文件, 建立​​House​​ 这个产品基类,代码如下;
package main

type House struct {
windowType string
doorType string
swimPool string
floor int
}

正像前文所说一眼,房子有窗户、门、游泳池、楼层等部分组成。

  1. 然后创建抽象创建者​​iBuilder.go​​ 文件,也是我们的建造者接口,分别定义 4 个​​set​​ 和 1 个​​getHouse()​​ 方法,代码如下:
package main

type IBuilder interface {
setWindowType()
setDoorType()
setNumFloor()
setSwimPool()
getHouse() House
}

func getBuilder(builderType string) IBuilder {
if builderType == "normal" {
return newNormalBuilder()
}

if builderType == "cottages" {
return newCottagesBuilder()
}
return nil
}
  1. 新建具体建造者:普通房子​​normalBuilder.go​​,在这个文件中,因为 Go 语言没有继承的概念,所以也需要我们定义跟​​House​​ 相同的结构体,然后实现 normalHouse 的构建 :
package main

type NormalBuilder struct {
windowType string
doorType string
swimPool string
floor int
}

func newNormalBuilder() *NormalBuilder {
return &NormalBuilder{}
}

func (b *NormalBuilder) setWindowType() {
b.windowType = "Wooden Window"
}

func (b *NormalBuilder) setDoorType() {
b.doorType = "Wooden Door"
}

func (b *NormalBuilder) setNumFloor() {
b.floor = 3
}

func (b *NormalBuilder) setSwimPool() {
b.swimPool = "None"
}

func (b *NormalBuilder) getHouse() House {
return House{
doorType: b.doorType,
windowType: b.windowType,
swimPool: b.swimPool,
floor: b.floor,
}
}
  1. 跟上一步同理,新建别墅具体建设者​​cottagesBuilder.go​​ 文件,代码如下:
package main

type cottagesBuilder struct {
windowType string
doorType string
swimPool string
floor int
}

func newCottagesBuilder() *cottagesBuilder {
return &cottagesBuilder{}
}

func (b *cottagesBuilder) setWindowType() {
b.windowType = "Glass Window"
}

func (b *cottagesBuilder) setDoorType() {
b.doorType = "Steel Security Door"
}

func (b *cottagesBuilder) setNumFloor() {
b.floor = 1
}

func (b *cottagesBuilder) setSwimPool() {
b.swimPool = "Swimming Pool"
}

func (b *cottagesBuilder) getHouse() House {
return House{
doorType: b.doorType,
windowType: b.windowType,
swimPool: b.swimPool,
floor: b.floor,
}
}
  1. 新建主管​​director.go​​ ,主管结构体内也是抽象建造者,其次主管有着​​setBuilder()​​ 和​​buildHouse()​​ 的职责,最后主管负责安排负责对象的建造次序,比如先确定门、窗、楼层,再考虑是否需要加装泳池。最终代码如下:
package main

type Director struct {
builder IBuilder
}

func newDirector(b IBuilder) *Director {
return &Director{
builder: b,
}
}

func (d *Director) setBuilder(b IBuilder) {
d.builder = b
}

func (d *Director) buildHouse() House {
d.builder.setDoorType()
d.builder.setWindowType()
d.builder.setNumFloor()
d.builder.setSwimPool()
return d.builder.getHouse()
}

6.新建一个 ​​main.go​​ 文件,测试我们的创建者模式是否正确:

package main

import (
"fmt"
)

func main() {

normalBuilder := getBuilder("normal")
cottagesBuilder := getBuilder("cottages")

director := newDirector(normalBuilder)
normalHouse := director.buildHouse()

fmt.Printf("Normal House Door Type: %s\n", normalHouse.doorType)
fmt.Printf("Normal House Window Type: %s\n", normalHouse.windowType)
fmt.Printf("Normal House SwimPool: %s\n", normalHouse.swimPool)
fmt.Printf("Normal House Num Floor: %d\n", normalHouse.floor)

director.setBuilder(cottagesBuilder)
cottagesHouse := director.buildHouse()

fmt.Printf("\nCottage House Door Type: %s\n", cottagesHouse.doorType)
fmt.Printf("Cottage House Window Type: %s\n", cottagesHouse.windowType)
fmt.Printf("Cottage House SwimPool: %s\n", cottagesHouse.swimPool)
fmt.Printf("Cottage House Num Floor: %d\n", cottagesHouse.floor)

}
  1. 最后,我们使用命令运行整个包​​go run .​

这是输出结果图:

一起学习 Go 语言设计模式之建造者模式_建造者模式_06

优缺点

优点:

  • 你可以分步创建对象, 暂缓创建步骤或递归运行创建步骤。
  • 生成不同形式的产品时, 你可以复用相同的制造代码。
  • 单一职责原则。 你可以将复杂构造代码从产品的业务逻辑中分离出来。

缺点:

  • 由于该模式需要新增多个类, 因此代码整体复杂程度会有所增加。

希望本文能对你有所帮助,如果喜欢本文,可以点个关注.下一篇文章见! 宇宙古今无有穷期,一生不过须臾,当思奋争。

参考链接:

标签:func,cottagesBuilder,House,建造,doorType,windowType,Go,设计模式
From: https://blog.51cto.com/yuzhou1su/5784582

相关文章

  • 设计模式专项练习
    A.模版模式:定义一个算法结构,而将一些步骤延迟到子类去实现B.备忘录模式:在不破坏封装的前提下,保持对象的内部状态C.抽象工厂:创建相关或依赖对象的家族,而无需指明具体类D.......
  • 设计模式--桥接模式
    代码:packagecom.by.bridge;interfacecolor{ StringgetColor();}classredimplementscolor{ @Override publicStringgetColor(){ //TODOAuto-generatedmeth......
  • 设计模式工厂方法:
    代码:packagecom.by;interfacelogger{ publicStringshow();}interfacefactory{ publicStringmakeLogger();}classfileloggerimplementslogger{ @Override pub......
  • 设计模式抽象工厂方法
    代码:packagecom.by;interfacelogger{ publicStringshow();}interfacefactory{ publicStringmakeLogger();}classfileloggerimplementslogger{ @Override pub......
  • 6 设计模式-适配模式
    适配器模式将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。模式中的角色1.目标接口(Target):......
  • Go语言channel用法总结
    channel的基本操作和注意事项channel存在3种状态:nil,未初始化的状态,只进行了声明,或者手动赋值为nilactive,正常的channel,可读或者可写closed,已关闭,关闭的channel并不是n......
  • Android软件中嵌入地图之一:Sogou地图
          在App中加入地图功能真是让人揪结,Google地图功能强大,但是有些国内的手机厂商去掉了手机系统中Google地图的相关库,所以这类机型将无法安装调用了Google地图的软......
  • .net core -利用 BsonDocumentProjectionDefinition 和Lookup 进行 join 关联 MongoDB
    前序   前段时间由于项目需要用到MongoDB,但是MongoDB不建议Collectionjoin 查询,网上很多例子查询都是基于linq进行关联查询。但是在stackoverflow找到一个例子,程......
  • Django之同时新增数据到两个数据库表与同时返回两个表的数据(插拔式)
    models:比如有以下三个模型fromdjango.dbimportmodels"""基类,其他类继承即可获得对应的字段"""classBaseModel(models.Model):updated_tm=models.DateT......
  • django--各个文件的含义
    当你创建项目或者应用后你是不是发现多了很多个文件,现在我们来看看各代表什么意思与你项目名相同的文件夹:是项目的管理功能目录,这个目录的名称因用户所创建的项目名称的不......