Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >建造者模式

建造者模式

作者头像
mingmingcome
发布于 2021-11-29 07:27:50
发布于 2021-11-29 07:27:50
38100
代码可运行
举报
运行总次数:0
代码可运行

begin 2018年9月12日08:08:17

建造者模式

定义

将一个复杂的对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。 ——《设计模式:可复用面向对象软件的基础》

建造者模式是一种对象创建型模式。

使用场景

从定义中的关键词“复杂的对象”就可以看出来,建造者模式适用于当我们在创建复杂的对象的时候。类似地,“同样的构建过程可以创建不同的表示”,即使客户端执行同样的步骤,也可以得到不一样表示的结果。如:建造者模式可以用于描述KFC如何创建套餐:套餐是一个复杂对象,它一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐等)等组成部分,不同的套餐有不同的组成部分,而KFC的服务员可以根据顾客的要求,一步一步装配这些组成部分,构造一个完整的套餐,然后返回给顾客。对于顾客来说,点餐的步骤是固定的,但是从服务员(即下文的指挥者)得到的套餐中主食和饮料是不一样的。

上面为其一,其二还可以作为拥有多个构造参数的需要使用重叠构造器的对象的一种解决方案,使得必要的参数在创建时传入,非必要的参数可以通过建造者的方法设置。后面会有详细的代码示例及分析。

角色

指挥者角色(Director):构建一个使用Builder接口的对象

抽象建造者角色(Builder):为创建一个Product对象的各个部件指定的抽象接口

具体建造者角色(ConcreteBuilder):实现Builder接口,构造和装配各个部件

产品角色(Product)

图示

建造者模式UML类图和序列图:

最终的结果是得到复杂的对象(Complex Object)。本来是客户端直接创建复杂对象,现在是应用建造者模式,通过指挥者指挥具体建造者生成复杂对象的组成部分,然后组装起一个完整的复杂对象。客户端不需要知道复杂对象的构造过程,只需要给指挥者指定具体建造者即可。

放大的类图:

代码示例

指挥者角色(Director.java):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Director {
	private Builder builder;
	
	// 指定建造者
	public Director(Builder builder) {
		this.builder = builder;
	}
	
	public Product construct() {
		builder.setPartA();
		builder.setPartB();
		builder.setPartC();
		return builder.build();
	}
}

指挥者对象创建时需要指定建造者。指挥者对象通常拥有一个指挥创建过程及返回复杂产品对象的方法,如construct()。

抽象建造者角色(Builder.java)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Builder {
	
	public void setPartA();
	
	public void setPartB();
	
	public void setPartC();
	
	public Product build();
}

抽象建造者接口确定产品由三个部分组成Part A、Part B、Part C,并声明一个组装产品的方法build()。

具体建造者角色(ConcreteBuilder.java):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ConcreteBuilder implements Builder {
	
	private Product product;
	
	public ConcreteBuilder() {
		product = new Product();
	}

	@Override
	public void setPartA() {
		product.setPartA("Part A");
	}

	@Override
	public void setPartB() {
		product.setPartB("Part B");		
	}

	@Override
	public void setPartC() {
		product.setPartC("Part C");
	}

	@Override
	public Product build() {		
		return product;
	}

}

产品角色(Product.java):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Product {
	private String partA;
	
	private String partB;
	
	private String partC;

	// 省略get、set及toString方法	
}

测试类(BuilderTest.java):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BuilderTest {

	public static void main(String[] args) {
		// 建造者
		Builder builder  = new ConcreteBuilder();
		// 指挥者
		Director director = new Director(builder);
		// 指挥者创建产品返回产品
		Product prod = director.construct();
		
		System.out.println(prod);  // 输出:Product [partA=Part A, partB=Part B, partC=Part C]
	}

}
实例

我们有一辆车,这个车有很多选项。我们可以用建造者模式建造车。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 指挥者和客户端
public class CarBuildDirector {
	private CarBuilder builder;
	
	public CarBuildDirector(CarBuilder builder) {
		this.builder = builder;
	}
	
	public Car construct() {
		return builder.setWheels(4)
				.setColor("Red")
				.build();
	}
	public static void main(String[] args) {
		CarBuilder builder = new CarBuilderImpl();
		
		CarBuildDirector carBuildDirector = new CarBuildDirector(builder);
		
		Car car = carBuildDirector.construct();
		
		System.out.println(car);
	}
}
// 产品
class Car {
	private int wheels;
	private String color;
	
	public Car() {}

	public int getWheels() {
		return wheels;
	}

	public void setWheels(int wheels) {
		this.wheels = wheels;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	@Override
	public String toString() {
		return "Car [wheels=" + wheels + ", color=" + color + "]";
	}
}
// 抽象建造者
interface CarBuilder {
	Car build();
	
	CarBuilder setWheels(int wheels);
	
	CarBuilder setColor(String color);
}
// 具体建造者
class CarBuilderImpl implements CarBuilder{
	private Car car;
	
	public CarBuilderImpl() {
		this.car = new Car();
	}

	@Override
	public Car build() {
		return car;
	}

	@Override
	public CarBuilder setWheels(int wheels) {
		car.setWheels(wheels);
		// 注意这里返回的是this对象,就是对象本身
		// 这样的话就可以在Director使用类似链式调用连续调用这个对象的所有方法
		return this;
	}

	@Override
	public CarBuilder setColor(String color) {
		car.setColor(color);
		return this;
	}
}

不同的地方是CarBuilderImpl.setWheels返回的是对象本身,这样我们在创建不同部分的时候可以连续调用对象的方法,代码简化,不好就是不习惯的话就不好理解了。

遇到多个构造器参数时考虑建造者模式

一个类表示包装食品外面的显示的营养成分标签。这些标签中有几个域是必须的:每份的含量、每罐的含量以及每份的卡路里,还有超过20个可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。

营养成分类(NutritionFacts.java):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class NutritionFacts {
	private final int servingSize;  //(ml)            required
	private final int servings;     //(per container) required
	private final int calories;     //                optional  
	private final int fat;          //(g)             optional  
	private final int sodium;       //(mg)            optional
	private final int carbohydrate; //(g)             optional  
	public NutritionFacts(int servingSize, int servings) {
		this(servingSize, servings, 0);
	}
	public NutritionFacts(int servingSize, int servings, int calories) {
		this(servingSize, servings, calories, 0);
	}
	public NutritionFacts(int servingSize, int servings, int calories, int fat) {
		this(servingSize, servings, calories, fat, 0);
	}
	public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
		this(servingSize, servings, calories, fat, sodium, 0);
	}
	public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
		this.servingSize = servingSize;
		this.servings = servings;
		this.calories = calories;
		this.fat = fat;
		this.sodium = sodium;
		this.carbohydrate = carbohydrate;
	}
	@Override
	public String toString() {
		return "NutritionFacts [servingSize=" + servingSize + ", servings=" + servings + ", calories=" + calories
				+ ", fat=" + fat + ", sodium=" + sodium + ", carbohydrate=" + carbohydrate + "]";
	}
}

这么多参数的构造函数,你怎么记得住。而且如果在未来的某天要增加一个参数什么的,还要修改现有的构造函数、然后添加新的构造函数。

在《Effective Java》第二版第2条的题目就是我上面的小标题,这个例子也是书上的例子。书上第三个解决方法:建造者模式。不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或静态工厂),得到一个builder对象。然后客户端在builder对象上调用类似与setter的方法,来设置每个相关的可选参数。最后,客户端调用哪个无参的build方法来生成不可变的对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class NutritionFactsWithBuilder {
	private final int servingSize;  
	private final int servings;     
	private final int calories;       
	private final int fat;            
	private final int sodium;       
	private final int carbohydrate; 
	
	public static class Builder {
		// Required parameters必要参数
		private final int servingSize;
		private final int servings;
		
		// Optional parameters - initialized to default values
		// 可选参数(初始化默认值)
		private int calories = 0;
		private int fat = 0;
		private int carbohydrate = 0;
		private int sodium = 0;
		// 创建builder时初始化必要参数
		public Builder(int servingSize, int servings) {
			this.servingSize = servingSize;
			this.servings = servings;
		}
		// 可选参数通过各自的方法设置
		public Builder calories(int val) {
			calories = val;
			return this;
		}
		
		public Builder fat(int val) {
			fat = val;
			return this;
		}
		
		public Builder carbohydrate(int val) {
			carbohydrate = val;
			return this;
		}
		
		public Builder sodium(int val) {
			sodium = val;
			return this;
		}
		
		public NutritionFactsWithBuilder build() {
			return new NutritionFactsWithBuilder(this);
		}
	}
	
	public NutritionFactsWithBuilder(Builder builder) {
		servingSize = builder.servingSize;
		servings = builder.servings;
		calories = builder.calories;
		fat = builder.fat;
		sodium = builder.sodium;
		carbohydrate = builder.carbohydrate;
	}

	@Override
	public String toString() {
		return "NutritionFactsWithBuilder [servingSize=" + servingSize + ", servings=" + servings + ", calories="
				+ calories + ", fat=" + fat + ", sodium=" + sodium + ", carbohydrate=" + carbohydrate + "]";
	}
}

这个缺点也很明显啊,多了一个builder类。

优点

1、客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。用户使用不同的具体建造者即可得到不同的产品对象,新增具体建造者符合“开闭原则”。

2、可以更精细地控制产品的创建过程。

缺点

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

总结

建造者模式,适用于创建有复杂内部结构的对象,对象属性之间相互依赖,且又可能要使用到一些其他不易得到的对象。

图是借用了维基百科英文版的,下面的有关Car代码例子也是。

维基百科Builder_pattern

end 2018年9月12日20:50:17

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-09-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
程序员内功心法【设计模式】之建造者模式
建造者模式构建复杂对象就像造汽车一样,是一个一个组件一个一个步骤创建出来的,它允许用户通过制定的对象类型和内容来创建他们,但是用户并不需要知道这个复杂对象是如何构建的,它只需要明白通过这样做我可以得到一个完整的复杂对象实例。
Java架构
2020/05/09
3090
程序员内功心法【设计模式】之建造者模式
建造者模式
对于建造者模式,我们首先来说明建造者模式是用来干嘛的。建造模式一般用于创建复杂对象,这些复杂对象的构建过程是稳定的,但是内部的构件通常要面临比较复杂的变化。怎么来解释呢?我们利用《大话设计模式》中的例子来解释,创建一个胖子和一个瘦子,我们需要画出头、手、脚、身体这几个部分,最没水平的写法是写两个类,一个胖子类,一个瘦子类,这不像我们一门面向对象语言所写出来的代码。通常我们可能会抽象出一个创建人的接口,并有画出头、手、脚、身体这几个抽象方法,胖子、瘦子分别来实现这几个方法,但是我们还需要将这几个方法组装起来,
用户1148394
2018/01/09
5590
建造者模式【建造者模式设计模式】
指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
高大北
2022/06/14
4730
【Effective Java】Ch2_创建销毁对象:Item2_当构造函数参数过多时考虑使用builder
静态工厂和构造函数都有一个限制:可选参数数量很大时,他们都不能很好地扩展。考虑一下这个例子:用一个类来表示袋装食品上的营养成分标签,这些标签有几个必选字段:每份的含量、每罐的份数、每份的卡路里;还有超过20个可选字段:总脂肪含量、饱和脂肪含量、转化脂肪含量、胆固醇含量、钠含量等等。大多数产品只有少数几个可选字段是非零值。
用户7886150
2020/12/15
4900
【地铁上的设计模式】--创建型模式:建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,它允许逐步创建复杂对象,同时分离出对象的构造过程和表示。该模式将构造复杂对象的过程分解为多个简单的步骤,使得相同的构造过程可以创建不同的表示形式。建造者模式通常适用于构造复杂对象或需要生成多个不同表示的对象。与其他创建型模式相比,建造者模式更加关注对象的构建过程,而不是创建过程。
喵叔
2023/04/28
2310
Java架构师教你写代码(二) - 使用建造者替代多参数的构造器
比如一个类,表示包装食品上的营养标签。 有些字段是必需的:净含量、毛重和每单位份量的卡路里, 还有 20 个可选字段,如:总脂肪、饱和脂肪、反式脂肪、胆固醇、钠… 大多食品只使用可选字段中的少数,且非零值。
JavaEdge
2021/02/22
6720
Java架构师教你写代码(二) - 使用建造者替代多参数的构造器
设计模式——建造者模式
设计模式——建造者模式
Java架构师必看
2021/05/14
4400
设计模式——建造者模式
设计模式5之建造者模式
一个复杂的对象往往由多个子部件按一定的步骤组成。例如,汽车由发动机、轮胎、方向盘等部件组成的。
Lvshen
2022/05/05
1630
设计模式5之建造者模式
用C++跟你聊聊“建造者模式”
会做菜吗?还是经常出去吃啊。做菜很重要的一点就是放调料,调料放好了,一盘菜也就活了。但是调料那么多,怎么能保证每次都放的合规格呢?且不说合不合格,能不漏掉油盐就很不错啦,要是一不小心忘了放盐,那就很尴尬了。
看、未来
2020/08/26
3230
用C++跟你聊聊“建造者模式”
「聊设计模式」之建造者模式(Builder)
🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅!
bug菌
2023/11/07
8831
「聊设计模式」之建造者模式(Builder)
建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,它通过将对象的构造与表示分离,使得同样的构建过程可以创建不同的对象。
码事漫谈
2024/12/20
1040
建造者模式
【C++】设计模式:建造者、原型、单例
建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
DevFrank
2024/07/24
870
java与es8实战之一:以builder pattern开篇
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于《java与es8实战》系列 《java与es8实战》系列是欣宸与2022年夏季推出的原创系列,如标题所述,该系列从一个java程序员视角去学习和实践elasticsearch的8.2版本,目标是与大家一起掌握与elasticsearch开发相关的技能,以应对实际应用中的需求和挑战 本篇概览 纵观欣宸过往各种系列文章,开篇无外乎两种套路 第一种是对该系列的主
程序员欣宸
2022/06/19
6620
java与es8实战之一:以builder pattern开篇
设计模式(4)[JS版]-JavaScript如何实现建造者模式?
建造者模式(Builder)可以将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。也就是说如果我们用了建造者模式,那么用户只需要指定需要建造的类型就可以得到所需要的东西,而具体建造的过程和细节不需要知道。建造者模式实际,就是一个指挥者,一个建造者和一个用户。用户调用指挥者,指挥者调用具体建造者工作,建造者建造出具体的东西给用户。
AlbertYang
2020/09/08
9890
设计模式(4)[JS版]-JavaScript如何实现建造者模式?
建造者模式
某游戏软件公司决定开发一款基于角色扮演的多人在线网络游戏,玩家可以在游戏中扮演虚拟世界中的一个特定角色,角色根据不同的游戏情节和统计数据(例如力量、魔法、技能等)具有不同的能力,角色也会随着不断升级而拥有更加强大的能力。
千羽
2021/12/29
4250
建造者模式
堆积木,建造者模式
假如一个对象的构建很复杂,需要很多步骤。则可以使用建造者模式,将其构建对象和组装成一个对象这两步给分开来。构建部分为(Builder)和组织部分(Director),实现了构建和装配的解耦。
BUG弄潮儿
2021/01/18
3820
堆积木,建造者模式
Effective Java 2.0_Item 2_中文版
静态工厂和构造函数有一个共同的限制:对于大量可选参数它们都不能很好的扩展。考虑这样一种情况:用一个类来表示包装食品上的营养成分标签。这些标签有几个字段是必须的——每份含量、每罐含量(份数)、每份的卡路里,二十个以上的可选字段——总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。大多数产品中这些可选字段中的仅有几个是非零值。
Tyan
2022/05/09
2760
设计模式-建造者模式
在软件开发过程中,我们经常面临着构建复杂对象的问题。这些对象可能具有多个属性,并且在创建过程中可能需要进行一系列复杂的初始化步骤。为了简化这个过程,我们可以使用设计模式中的建造者模式。
架构狂人
2023/10/10
2970
设计模式-建造者模式
Effective Java 2.0_中英文对照_Item 2
Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters. Consider the case of a class representing the Nutrition Facts label that appears on packaged foods. These labels have a few required fields—serving size, servings per container, and calories per serving and over twenty optional fields—total fat, saturated fat, trans fat, cholesterol, sodium, and so on. Most products have nonzero values for only a few of these optional fields.
Tyan
2022/05/09
3420
建造者模式(Builder Pattern)
用户只需要给出指定复杂对象的类型和内容; 建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
用户1205080
2018/11/29
4950
建造者模式(Builder Pattern)
相关推荐
程序员内功心法【设计模式】之建造者模式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档