乐趣区

关于go:Go-语言设计模式之建造者模式

前言

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

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

思考如下问题

假如让咱们思考如何创立一个屋宇对象。建造一栋简略的屋宇,你须要建造地板和四面的强,装置房门和窗户,而后再建造一个丑陋的屋顶。

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

最简略的办法就是扩大屋宇的积攒,而后创立一系列涵盖所有参数组合的子类。随着房子越简单,子类越多,任何新增的参数都会让这个层次结构更加简单。

另外一种形式则无需生成子类,咱们能够在屋宇基类中创立一个蕴含所有可能参数的超级构造函数,并用它来管制屋宇对象,这种办法的确能够防止生成子类,但它却会造成另外一个问题——结构函数参数太多。

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

但并不是所有的房子都须要游泳池,导致绝大部分的参数都没有应用,使得 构造函数的申明简单,调用不整洁

解决形式就是明天要介绍的建造者模式。

建造者模式概念

建造者模式(Builder Pattern),又称生成器模式,是较为简单的创建者模式,它将客户端与蕴含多个组成部分的简单对象的创立过程拆散,客户端无需晓得简单对象的外部组成部分与拆卸形式,只须要晓得所需的建造者类型即可。

定义:将一个简单对象的构建与它的示意拆散,使得同样的构建过程能够创立不同的示意。

结构图如下:

由上图能够晓得,建造者模式蕴含 4 个角色:

  • Builder 形象建造者:它为创立一个产品 Product 对象的各个部件指定形象接口,这个接口个别包含两类办法:
  • buildPartX():用于创立简单对象的各个部件
  • getResult():用于返回简单对象
  • ConcreteBuilder 具体建造者:它实现了 Builder 接口,实现各个部件的具体结构和拆卸办法,定义并明确其所创立的简单对象,也能够提供一个办法返回创立好的简单产品对象
  • Product 产品角色:它是最终被构建的简单对象,蕴含多个组成部件,具体建造者创立该产品的外部示意并定义其拆卸过程。
  • Director 主管、指挥者:指挥者又被称为导演类,定义调用结构步骤的程序。它负责安顿负责对象的建造秩序,指挥者和形象建造者之间存在关联关系,能够在其 construct() 建造办法中调用建造者对象的部件结构与拆卸办法,实现负责对象的建造。

Go 代码示例

代码组织构造如下:

  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 .

这是输入后果图:

优缺点

长处:

  • 你能够分步创建对象,暂缓创立步骤或递归运行创立步骤。
  • 生成不同模式的产品时,你能够复用雷同的制作代码。
  • 繁多职责准则。你能够将简单结构代码从产品的业务逻辑中分离出来。

毛病:

  • 因为该模式须要新增多个类,因而代码整体复杂程度会有所增加。

心愿本文能对你有所帮忙,如果喜爱本文,能够点个关注.

下一篇文章见!宇宙古今无有穷期,毕生不过顷刻,当思奋争。

参考链接:

  • 建造者设计模式(生成器模式)
  • 《设计模式的艺术》,作者:刘伟
退出移动版