设计模式-生成器模式

20191107184531.jpg

前言

无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,他们拥有多个组成部分,以汽车🚗为例,它包括车轮、方向盘、发动机等部件。对于用户而言,无须知道这些部件的装配细节,它几乎不会使用单独部件,而是使用一辆完整的汽车,可以通过生成器模式对其进行设计与描述,生成器模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部构造细节。

在软件开发中,也存在大量类似汽车一样的复杂对象,而对象的属性相当于汽车的部件,建造产品的过程就相当于组合部件的过程。由于组合部件的过程很复杂,因此,这些部件的组合过程往往被“外部化”到一个称作生成器的对象里,生成器返回给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性以及他们的组装方式,这就是生成器模式的愿景。

定义

生成器模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

生成器模式是一步一步创建一个复杂的对象,它允许用户只通过制定复杂对象的类型和内容就可以构建他们,用户不需要知道内部的具体构建细节,建造者模式属于对象创建型。

使用时机

当对象拥有好几种属性,且需要避免构造器伸缩时使用,与工厂模式运用场景不同之处在于:当创建过程仅仅一步到位,使用工厂模式,如果需要分步进行,则考虑使用生成器模式。

本质

生成器模式的本质,就是将构造函数中的参数列表方法化,长长的参数列表,无论是面向对象还是函数式编程,都是大忌,该模式主要就是为了解决该问题。

实现

C++实现

#include <iostream>

 class FriedRice {
     public:
         class FriedRiceBuilder;
         void showFlavors() {
             std::cout << _size;
             if (_egg) std::cout << "-egg";
             if (_beef) std::cout << "-beef";
             if (_onion) std::cout << "-onion";
             std::cout << std::endl;
         }
     private:
         FriedRice(int size):_size(size){}

         int _size = 0;
         bool _egg = false;
         bool _beef = false;
         bool _onion = false;
 };

 class FriedRice::FriedRiceBuilder {
     public:
         FriedRiceBuilder(int size) {_friedrice = new FriedRice(size);}
         FriedRiceBuilder& AddEgg() {_friedrice->_egg = true; return *this;}
         FriedRiceBuilder& AddBeef() {_friedrice->_beef = true; return *this;}
         FriedRiceBuilder& AddOnion() {_friedrice->_onion = true; return *this;}
         FriedRice* Build() {return _friedrice;}
     private:
         FriedRice* _friedrice;
 };

 int main() {
     FriedRice* friedRice = FriedRice::FriedRiceBuilder(7).AddEgg().AddBeef().AddOnion().Build();
     friedRice->showFlavors();
     return 0;
 }

golang实现

package builder

type Builder interface {
	part1()
	part2()
	part3()
}

type Director struct {
	builder Builder
}

func NewDirector(b Builder) *Director {
	return &Director{builder: b}
}

func (d *Director) Constructor() {
	d.builder.part1()
	d.builder.part2()
	d.builder.part3()
}

type FirstBuilder struct {
	result string
}

func (fb *FirstBuilder) part1() {
	fb.result += "1"
}

func (fb *FirstBuilder) part2() {
	fb.result += "2"
}

func (fb *FirstBuilder) part3() {
	fb.result += "3"
}

func (fb *FirstBuilder) Result() string {
	return fb.result
}

type SecondBuilder struct {
	result string
}

func (sb *SecondBuilder) part1() {
	sb.result += "11"
}

func (sb *SecondBuilder) part2() {
	sb.result += "22"
}

func (sb *SecondBuilder) part3() {
	sb.result += "33"
}

func (sb *SecondBuilder) Result() string {
	return sb.result
}

测试用例

package builder

import (
	"reflect"
	"testing"
)

func TestFirstBuilder_Result(t *testing.T) {
	fb := FirstBuilder{}
	d := NewDirector(&fb)
	d.Constructor()
	if reflect.ValueOf(d.builder).Interface().(*FirstBuilder).Result() != "123" {
		t.Error("first builder test failed")
	}
}

func TestSecondBuilder_Result(t *testing.T) {
	sb := SecondBuilder{}
	d := NewDirector(&sb)
	d.Constructor()
	if reflect.ValueOf(d.builder).Interface().(*SecondBuilder).Result() != "112233" {
		t.Error("second builder test failed")
	}
}

测试结果

go test -v .
=== RUN   TestFirstBuilder_Result
--- PASS: TestFirstBuilder_Result (0.00s)
=== RUN   TestSecondBuilder_Result
--- PASS: TestSecondBuilder_Result (0.00s)
PASS
ok      design-patterns/builder 0.521s

优缺点

优点

  • 松散耦合
    生成器模式可以用同一个构建算法构建出表现上完全不同的产品,实现产品构建和产品表现上的分离。生成器模式正是把产品构建的过程独立出来,使它和具体产品的表现松散耦合,从而使得构建算法可以复用,而具体产品表现也可以灵活地、方便地扩展和切换。
  • 可以很容易地改变产品的内部表示
    在生成器模式中,由于FriedRiceBuilder对象只是提供接口给FriedRice使用,那么具体的部件创建和装配方式是被FriedRiceBuilder接口隐藏了的,FriedRice并不知道这些具体的实现细节。这样一来,要想改变产品的内部表示,只需要切换FriedRiceBuilder的具体实现即可,不用管FriedRice,因此变得很容易。
  • 更好的复用性
    生成器模式很好地实现了构建算法和具体产品实现的分离。这样一来,使得构建产品的算法可以复用。同样的道理,具体产品的实现也可以复用,同一个产品的实现,可以配合不同的构建算法使用。

缺点

  • 生成器模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用生成器模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体生成器类来实现这种变化,导致系统变得很庞大。
posted @ 2019-11-07 19:27  Rimond_Jing  阅读(541)  评论(0编辑  收藏  举报