1、引言
在软件开发的过程中,有时需要创建一个复杂对象,并且这个复杂对象由其各部分子对象通过一定的步骤组合而成。就像一台电脑,一部手机,一辆汽车,都是通过一定的步骤组合而成,这里所说的建造者模式也是这个意思,下面我们一起来看看建造者模式的介绍。
2、建造者模式详细介绍
建造者模式是怎么定义的呢,又有什么作用,解决了什么问题,我们该怎么使用呢,下面我们将揭开建造者模式的神秘面纱。
2.1、定义
- 建造者模式(Builder Pattern):
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2.2、作用
在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
- 用户只需要给出指定复杂对象的类型和内容;
- 建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
2.3、解决的问题
该模式解决了:
抽象工厂模式解决了“系列产品”的需求变化,而 建造者模式解决的是 “产品部分” 的需要变化。
- 方便用户创建复杂的对象(不需要知道实现过程)
- 代码复用性 、封装性(将对象构建过程和细节进行封装 & 复用)
例子:造汽车 & 买汽车
1.不同的工厂(建造者):负责制造相应的汽车(组装过程和细节在工厂内)
2. 销售者(指挥者):卖什么车(车的型号和内容,告诉厂家,这个车销量好,多造点)
3. 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了 (不需要知道汽车是怎么组装的(车轮、车门、发动机、方向盘等等))
2.4、模式的原理
下面是该模式的UML类图:
通过上图我们不难发现,建造者模式中的角色如下:
- 产品(Product):产品类,规定了产品的组成部件。这里的产品是同一类产品。
- 抽象建造者(Builder):为一个产品(Product)对象的各个部件提供了一个抽象接口
- 具体建造者(ConcreteBuilder):实现Builder接口,根据具体的要求建造和装配各个部件
- 指挥者(Director):用来指挥创建过程,直接和客户(Client)进行需求沟通,构建了一个使用builder接口的对象。
具体流程就是:
- 指挥者(Director)和客户交流,获取客户需要什么产品
- 指挥者交流后知道了产品需求,根据需求来决定产品部件的建造,统一分给各部门相应的建造者(Builder)
- 各部门再安排给具体的生产小组(ConcreteBuilder),造出部件
- 最终完成产品的生产,告诉老板(指挥者Director)做好了
2.5、类图实现
下面我们来实现类图中的代码:
产品(Product)类:
/// <summary>
/// 产品类,由多个部件组成
/// </summary>
public class Product
{
/// <summary>
/// 产品部件集合
/// </summary>
IList<string> parts = new List<string>();
/// <summary>
/// 添加产品部件
/// </summary>
/// <param name="part"></param>
public void Add(string part)
{
parts.Add(part);
}
public void Show()
{
Console.WriteLine("\n产品 创建 ==>>>");
foreach (string part in parts)
{
Console.WriteLine(part);
}
}
}
抽象建造者(Builder)类:
/// <summary>
/// 抽象建造者类
/// 确定产品的两个部件PartA、PartB,
/// 并声明一个得到产品建造后果的方法GetResult.
/// </summary>
public abstract class Builder
{
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract Product GetResult();
}
具体建造者(ConcreteBuilder)
/// <summary>
/// 具体建造者类
/// </summary>
class ConcreteBuilder1 : Builder
{
private Product product = new Product();
public override void BuildPartA()
{
product.Add("部件A");
}
public override void BuildPartB()
{
product.Add("部件B");
}
public override Product GetResult()
{
return product;
}
}
/// <summary>
/// 具体建造者类
/// </summary>
public class ConcreteBuilder2 : Builder
{
private Product product = new Product();
public override void BuildPartA()
{
product.Add("X部件");
}
public override void BuildPartB()
{
product.Add("Y部件");
}
public override Product GetResult()
{
return product;
}
}
指挥者(Director)类:
/// <summary>
/// 指挥者类
/// </summary>
class Director
{
/// <summary>
/// 用来指挥创建过程
/// </summary>
/// <param name="builder"></param>
public void Construct(Builder builder)
{
builder.BuildPartA();
builder.BuildPartB();
}
}
测试下:
Director director = new Director();
Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();
director.Construct(b1);
Product p1 = b1.GetResult();
p1.Show();
director.Construct(b2);
Product p2 = b2.GetResult();
p2.Show();
测试结果如下:
产品 创建 ==>>>
部件A
部件B
产品 创建 ==>>>
X部件
Y部件
通过类图实现我想你已经对建造者模式有了了解,下面我们通过一个例子来进一步了解下。
2.6、C#举例
- 背景:小成希望去电脑城买一台组装的台式主机
- 过程:
- 电脑城老板(Diretor)和小成(Client)进行需求沟通(买来打游戏?学习?看片?)
- 了解需求后,电脑城老板将小成需要的主机划分为各个部件(Builder)的建造请求(CPU、主板blabla)
- 指挥装机人员(ConcreteBuilder)去构建组件;
- 将组件组装起来成小成需要的电脑(Product)
技术背景,电脑由一些部件组成:
public class Computer
{
//电脑组件的集合
private List<string> parts = new List<string>();
//用于将组件组装到电脑里
public void Add(string part)
{
parts.Add(part);
}
public void Show()
{
foreach (string part in parts)
{
Console.WriteLine("部件{0}安装完毕...", part);
}
Console.WriteLine("电脑组装完成,请验收");
}
}
装机人员都知道电脑的组装步骤:
public abstract class ComputerBuilder
{
//第一步:装CPU
//声明为抽象方法,具体由子类实现
public abstract void BuildCPU();
//第二步:装主板
//声明为抽象方法,具体由子类实现
public abstract void BuildMainboard();
//第三步:装硬盘
//声明为抽象方法,具体由子类实现
public abstract void BuildHD();
//返回产品的方法:获得组装好的电脑
public abstract Computer GetComputer();
}
某电脑商家的组装人员:
//某装机人员
public class ConcreteComputerBuilder :ComputerBuilder
{
//创建产品实例
Computer computer = new Computer();
//组装产品
public override void BuildCPU()
{
computer.Add("组装CPU");
}
public override void BuildMainboard()
{
computer.Add("组装主板");
}
public override void BuildHD()
{
computer.Add("组装硬盘");
}
/// <summary>
/// 返回组装成功的电脑
/// </summary>
/// <returns></returns>
public override Computer GetComputer()
{
return computer;
}
}
某商家老板:
public class ComputerDirector
{
//指挥装机人员组装电脑
public void Construct(ComputerBuilder builder)
{
builder.BuildCPU();
builder.BuildMainboard();
builder.BuildHD();
}
}
测试:
//逛了很久终于发现一家合适的电脑店
//找到该店的老板和装机人员
ComputerDirector director = new ComputerDirector();
ComputerBuilder builder = new ConcreteComputerBuilder();
//沟通需求后,老板叫装机人员去装电脑
director.Construct(builder);
//装完后,组装人员搬来组装好的电脑
Computer computer = builder.GetComputer();
//组装人员展示电脑给小成看
computer.Show();
结果:
部件组装CPU安装完毕...
部件组装主板安装完毕...
部件组装硬盘安装完毕...
电脑组装完成,请验收
电脑组装好了,我想你已经理解建造者模式的原理了,下面我们看看它有什么优缺点。
3、建造者模式优缺点
建造者模式优缺点的优缺点如下:
优点
易于解耦
将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。
易于精确控制对象的创建
将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
易于拓展
增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。
综述
每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
4、适用场景
这里我们来一起分析它的适用场景:
- 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品;
5、应用举例(unity)
产品父类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIView
{
protected Text mText;
protected Text mBtnText;
protected Image mImage;
public void Init(Transform transform)
{
mText = Instantiate(transform, "UIView/MyText").GetComponent<Text>();
Button button = Instantiate(transform, "UIView/Button").GetComponent<Button>();
mBtnText = transform.Find("Button(Clone)/Text").GetComponent<Text>();
mImage = Instantiate(transform, "UIView/Image").GetComponent<Image>();
}
protected GameObject Load(string path)
{
return Resources.Load<GameObject>(path);
}
protected GameObject Instantiate(Transform parent, string path)
{
GameObject go = GameObject.Instantiate(Load(path));
GameObject goNew = GameObject.Find("");
go.transform.SetParent(parent, false);
//go.transform.localScale = Vector3.one;
//go.transform.position = go.transform.position;
return go;
}
}
产品类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Product
{
List<UIView> parts = new List<UIView>();
public void Add(UIView go)
{
if (go is ConcreteBuilder1)
parts.Add(go as ConcreteBuilder1);
if (go is ConcreteBuilder2)
parts.Add(go as ConcreteBuilder2);
}
public void Show()
{
foreach (UIView go in parts)
{
if (go is ConcreteBuilder1)
{
ConcreteBuilder1 b1 = go as ConcreteBuilder1;
b1.BuilderMethodA();
}
if (go is ConcreteBuilder2)
{
(go as ConcreteBuilder2).BuilderMethodA();
}
}
}
}
建造者:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class Builder : UIView
{
public abstract void BuilderMethodA();
public abstract Product GetResult();
}
具体建造者:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//具体建造者
public class ConcreteBuilder1 : Builder
{
private Product product = new Product();
public override void BuilderMethodA()
{
mText.text = "建造者A";
mBtnText.text = "建造者A按钮";
mImage.color = Color.red;
}
public override Product GetResult()
{
return product;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//具体建造者
public class ConcreteBuilder2 : Builder
{
private Product product = new Product();
public override void BuilderMethodA()
{
mText.text = "建造者B";
mBtnText.text = "建造者B按钮";
mImage.color = Color.green;
}
public override Product GetResult()
{
return product;
}
}
指导者
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Director
{
public void Construct(Builder builder)
{
builder.BuilderMethodA();
}
}
测试类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour {
void OnGUI()
{
if (GUI.Button(new Rect(100, 100, 120, 50), "按钮1"))
{
Director director = new Director();
Builder b1 = new ConcreteBuilder1();
b1.Init(this.transform);
director.Construct(b1);
Product p1 = b1.GetResult();
p1.Show();
}
if (GUI.Button(new Rect(100, 200, 120, 50), "按钮2"))
{
Director director = new Director();
Builder b2 = new ConcreteBuilder2();
b2.Init(this.transform);
director.Construct(b2);
Product p2 = b2.GetResult();
p2.Show();
}
}
}
我们只需要把Test挂在Canvas上即可,资源创建及运行结果如图所示:
6、总结
到这里,建造者模式的介绍就结束了,建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使的同样的构建过程可以创建不同的表示。建造者模式的本质是使组装过程(用指挥者类进行封装,从而达到解耦的目的)和创建具体产品解耦,使我们不用去关心每个组件是如何组装的。
7、unity工程下载
在文章的最后我们给出上述例子的工程下载链接,方便朋友们探讨交流!本次演示版本Unity5.6.3f1(64-bit),VS2015需要下载的朋友,请点击此处下载。
The End
好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!
喜欢的朋友们,请帮顶、点赞、评论!您的肯定是我写作的不竭动力!
相关阅读
C# 23种设计模式(unity演示)