设计模式解密(6) - 建造者模式(生成器模式)

1、简介

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

分解一下定义:

  1、复杂对象的表示;
  2、复杂对象的构建过程;
  3、可分离的通用构建过程,也适用于其它复杂对象的表示;
  4、适用于一些基本部件不会变,而其组合经常变化的时候。

英文:Builder

类型:创建类模式

2、原理及组成

:类图

四个要素

  产品类:一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。在本类图中,产品类是一个具体的类,而非抽象类。

      实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。

  抽象建造者:引入抽象建造者的目的,是为了将建造的具体过程交与它的子类来实现。这样更容易扩展。

        一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品。

  建造者:实现抽象类的所有未实现的方法,具体来说一般是两项任务:组建产品;返回组建好的产品。

  导演类:负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类。

      一般来说,导演类被用来封装程序中易变的部分。

其中最重要的两个部分

  1、一个部分是Builder接口,这里是定义了如何构建各个部件,也就是知道每个部件功能如何实现 
  2、另外一个部分是Director,Director是知道如何组合来构建产品,也就是说Director负责整体的构建算法,而且通常是分步骤地来执行,也就是说如何组装这些部件。

不管如何变化,建造模式都存在这么两个部分,一个部分是部件构造。另一个部分是整体构建的算法。 
再直白点说,建造模式的重心在于分离构建算法和具体的构造实现,从而使得构建算法可以重用。具体的构造实现可以很方便地扩展和切换,从而可以灵活地组合来构造出不同的产品对象。

3、实例引入

背景:模拟生产各种笔(这里假设笔的零件生产是有顺序的:笔芯 -> 笔壳 -> 组装)

创建抽象产品类 -- 笔

package com.designpattern.builder;
/**
 * 笔  -- 抽象产品类
*/
public abstract class Pen {
	/**笔芯**/
	private String cartridge;
	/**外壳**/
	private String shell;

	public String getCartridge() {
		return cartridge;
	}
	public void setCartridge(String cartridge) {
		this.cartridge = cartridge;
	}
	public String getShell() {
		return shell;
	}
	public void setShell(String shell) {
		this.shell = shell;
	}
}

创建抽象建造者(笔builder) 接口

package com.designpattern.builder;
/**
 * 抽象建造者  -- 笔builder
*/
public abstract interface PenBuilder {
	/**
	 * 生产笔芯
	 */
	abstract void buildCartridge();
	/**
	 * 生产外壳
	 */
	abstract void buildShell();
	/**
	 * 组装笔
	 */
	abstract Pen buildPen();
}

创建具体产品类(圆珠笔)

package com.designpattern.builder;
/**
 * 具体产品类 -- 圆珠笔
*/
public class BallpointPen extends Pen{
	public BallpointPen(){
		System.out.println("生产组装圆珠笔");
	}
}

创建具体产品类(画笔)

package com.designpattern.builder;
/**
 * 具体产品类 -- 画笔
*/
public class BrushPen extends Pen{
	public BrushPen(){
		System.out.println("生产组装画笔");
	}
}

创建建造者(具体)  -- 圆珠笔builder

package com.designpattern.builder.impl;

import com.designpattern.builder.BallpointPen;
import com.designpattern.builder.Pen;
import com.designpattern.builder.PenBuilder;

/**
 * 建造者(具体)  -- 圆珠笔builder
*/
public class BallpointPenBuilder implements PenBuilder{

	Pen pen;
	public BallpointPenBuilder(){
		pen = new BallpointPen();
	}

	@Override
	public void buildCartridge() {
		pen.setCartridge("圆珠笔笔芯");
	}

	@Override
	public void buildShell() {
		pen.setShell("圆珠笔外壳");
	}

	@Override
	public Pen buildPen() {
		return pen;
	}

}

创建建造者(具体)  -- 画笔builder

package com.designpattern.builder.impl;

import com.designpattern.builder.BrushPen;
import com.designpattern.builder.Pen;
import com.designpattern.builder.PenBuilder;

/**
 * 建造者(具体)  -- 画笔builder
*/
public class BrushPenBuilder implements PenBuilder{

	Pen pen;
	public BrushPenBuilder(){
		pen = new BrushPen();
	}

	@Override
	public void buildCartridge() {
		pen.setCartridge("画笔笔芯");
	}

	@Override
	public void buildShell() {
		pen.setShell("画笔外壳");
	}

	@Override
	public Pen buildPen() {
		return pen;
	}

}

创建导演类  Director

package com.designpattern.builder;
/**
 * 导演类  Director
*/
public class PenDirector {

	public Pen constructPen(PenBuilder pen_builder){
		//生产笔芯
		pen_builder.buildCartridge();
		//生产外壳
		pen_builder.buildShell();
		//组装笔
		return pen_builder.buildPen();
	}
}

下面测试一下

package com.designpattern.builder;

import com.designpattern.builder.impl.BallpointPenBuilder;
import com.designpattern.builder.impl.BrushPenBuilder;

/**
 * 测试
*/
public class Test {
	public static void main(String[] args) {
		PenDirector director = new PenDirector();
		Pen ballpointpen = director.constructPen(new BallpointPenBuilder());
		Pen brushpen = director.constructPen(new BrushPenBuilder());
	}
}

结果:

生产组装圆珠笔
生产组装画笔

4、解决的问题

笔的种类不止这几种,还有很多,使用建造者模式可以将一类产品的构建过程和细节进行封装(实现代码的复用)与产品的表示进行分离,即:使用同样的构建过程可以创建不同的产品;

在各个建造者类,只需定义生产零件方法,具体的生产顺序定义在导演类中;

5、优缺点

优点:

    封装性很好:使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在导演类中对整体而言可以取得比较好的稳定性。

    扩展性很好:建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。

    可以有效控制细节风险:由于具体的建造者是独立的,因此可以对建造者过程逐步细化,而不对其他的模块产生任何影响。

缺点:

  建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

  如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

6、应用场景

  建造者模式 --- 建造具有具体细节的产品

  1、需要生成的产品对象有复杂的内部结构,这些产品对象具备共性。

  2、需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。

  ........

7、与其他模式对比

  我们可以看到,建造者模式与工厂模式是极为相似的,总体上,建造者模式仅仅只比工厂模式多了一个“导演类”的角色。在建造者模式的类图中,假如把这个导演类看做是最终调用的客户端,那么图中剩余的部分就可以看作是一个简单的工厂模式了。

  这里先回归一下定义:

      工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类;

      建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示;

  与工厂模式相比,建造者模式一般用来创建更为复杂的对象,因为对象的创建过程更为复杂,因此将对象的创建过程独立出来组成一个新的类——导演类。也就是说,工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。

  工厂模式一般都是创建一个产品,注重的是把这个产品创建出来就行,只要创建出来,不关心这个产品的组成部分。

  建造者模式也是创建一个产品,但是不仅要把这个产品创建出来,还要关心这个产品的组成细节, 组成过程。

8、总结

  建造者模式最主要功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了;

  而工厂方法则重点是创建,你要什么对象就创造一个对象出来,组装顺序则不是他关心的。

  PS:建造模式的关键是导演角色,这个角色掌握了零件对象的状态和产品的整体组装蓝图。没有了这个角色,建造模式就不是建造模式。当然,导演角色可以同时持有很多种蓝图,按照需要给出完全不同的组装结果。

  PS2:源码地址   https://github.com/JsonShare/DesignPattern/tree/master

时间: 2024-10-09 16:34:50

设计模式解密(6) - 建造者模式(生成器模式)的相关文章

设计模式解密(22)- 访问者模式 - 扩展篇(分派的概念)

前言:访问者模式拆分  访问者模式基础篇 :http://www.cnblogs.com/JsonShare/p/7380772.html  访问者模式扩展篇 - 分派的概念: http://www.cnblogs.com/JsonShare/p/7381705.html 1.分派的概念 变量被声明时的类型叫做变量的静态类型(Static Type),有些人又把静态类型叫做明显类型(Apparent Type):而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type).比如:

设计模式第3篇:生成器模式(Builder)

一.生成器模式要解决的问题 生成器模式主要解决工厂方法模式和抽象工厂模式在所创建的对象包含大量的属性时所面临的问题: 当客户端程序向工厂类传递大量参数时很容易发生错误,因为很多参数的类型有可能是相同的,并且也很难保证传递参数的顺序. 在传递参数时有些参数是可选的,但是在工厂设计模式中,所有的餐宿都必须被传递,即使可选参数也应该传递null值. 如果所要创建的对象本身很复杂,那么工厂类也会很复杂,过于复杂的程序设计不易于团队开发. 二.生成器模式的要点 首先需要在外部类中创建一个静态的生成器类,将

设计模式解密(12)- 桥接模式

1.简介 定义:将抽象部分与实现部分分离,使它们都可以独立的变化. 主要解决:在多维可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活. 何时使用:实现系统可能有多个角度分类,每一种角度都可能变化. 如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合. 注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了. 英文:bridge 类型:结构型 2.问题引入 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面

设计模式 | 建造者模式/生成器模式(builder)

定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 结构:(书中图,侵删) 一个产品类 一个指定产品各个部件的抽象创建接口 若干个实现了各个部件的具体实现的创建类 一个指挥者,用于控制具体的创建过程,包含一个抽象创建接口的引用 实例: 书中使用了游戏中创建人物的例子,人都有头.四肢.躯干这些固定组成部分,但是高矮胖瘦各不相同. 我瞬间就想到了捏脸系统,虽然说捏脸系统把人物的各种细节都交给了用户设置,细节到嘴唇的弧度都可以用户调节. 但实际上创建过程还是固定不变的,

设计模式——建造者模式/生成器模式(C++实现)

1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class STProduct 7 { 8 public: 9 void setA(string str) 10 { 11 m_a = str; 12 } 13 14 void setB(string str) 15 { 16 m_b = str; 17 } 18 19 void setC(string str) 20 { 21 m_c =

设计模式解密(22)- 访问者模式

前言:访问者模式拆分  访问者模式基础篇 :http://www.cnblogs.com/JsonShare/p/7380772.html  访问者模式扩展篇 - 分派的概念: http://www.cnblogs.com/JsonShare/p/7381705.html 1.简介  定义:表示一个作用于其对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 主要解决:稳定的数据结构和易变的操作耦合问题.就是把数据结构和作用于结构上的操作解耦合,使得操作集合可

设计模式解密(18)- 原型模式

1.简介 定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象. 功能:①是通过克隆来创建新的对象实例:②是为克隆出来的新的对象实例复制原型实例属性的值. 本质:通过克隆来创建新的对象实例. 英文:Prototype 类型:创建型 2.类图及组成 (引)类图: 组成: Prototype:声明一个克隆自身的接口,用来约束想要克隆自己的类,要求它们都要实现这里定义的克隆方法. ConcretePrototype:实现Prototype接口的类,这些类真正实现了克隆自身的功能. C

设计模式解密(3)- 策略模式

1.简介 定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换.策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装.因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可. 2.实例引入 背景:模拟商城的促销活动,旨在了解模式 先定义一个活动接口: package com.designpattern.strategy; /** * 类说明 :促销活动抽象接口 */ public interface AbstractSaleActivi

设计模式解密(21)- 解释器模式

1.简介 定义:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子. 主要解决:对于一些固定文法构建一个解释句子的解释器. 本质:分离实现,解释执行.Interpreter模式其实就是一种简单的语法解释器构架. 英文:Interpreter 类型:行为型 2.类图及组成 (引)类图: 组成: AbstractExpression(抽象表达式):定义解释器的接口,约定解释器的解释操作. TerminalExpression(终结符表达式):用来实现语法规则中和

设计模式解密(5)- 外观模式(门面模式)

1.简介 外观模式提供了一个统一的接口,用来访问子系统中的一群接口.外观定义了一个高层接口,让子系统更容易使用. 使用外观模式时,我们创建了一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以直接通过外观类来调用内部子系统中方法,从而外观模式让客户和子系统之间避免了紧耦合. 外观模式的目的不是为了给子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统. 外观模式的本质是:封装交互,简化调用. 英文:Facade 类型:结构型模式