建造者(生成器)模式
含义:生成器模式是一种创建型模式,使你能够分步奏创建复杂对象。可是使用相同的创建代码生成不同类型和形式的对象。
看图我们就能很好地理解,图中就是工厂中的流水线模式,建造者就好比整条流水线,通过流水线上每个装配点的工人将一个个产品零件组装整合成一个完整的产品即可。
将对象构造代码从具体产品类中抽取出来,并将其放在一个名为生成器的独立对象中。
它可以让你能够分步奏创建复杂对象,不允许其他对象访问正在创建中的产品。利用相同的物料,不同的组装所产生出的具体内容,就是建造者模式的最终体现。
将一个复杂的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
生成器模式结构
- 生成器(Builder)接口声明在所有类型生成器中通用的产品构造步奏。
- 具体生成器(Concrete Builders)提供构造过程的不同实现,具体生成器也可以构造不遵循通用接口的产品。
- 产品(Products)是最终生成的对象。由不同生成器构造的产品无需数据同一类层次结构或接口。
- 主管(Director)类定义调用构造步奏的顺序,这样就可以创建和复用特定的产品配置。
建造者模式主要解决的问题是在软件系统中,有时候面临着一个复杂对象的创建工作,其通常由各部分的子对象用一定的过程构成;由于需求的变化,这个复杂对象的各个部分经常面临着重大的变化,但是将它们组合在一起的过程却相对稳定。
Demo展示
这里我就拿极速物流公司中发货的过程来举例,希望大家能对建造者模式有一个很好的理解。
小A去物流公司发货,他手里面的货物要发送到3个不同的地方,物流公司会根据不同地区有不同的收费标准。可是对于小A来说,他不需要管那些,只需要将三件东西交给收货员,填写好各自货物的收件地址就OK,收货员通过计算出不同收件地址的收费情况给出报价单,告诉小A应付多少钱,小A只需要付款就可以。
这里不同地区收费不一样给出报价单的业务就可以使用我们刚刚学习到的建造者模式来实现,由于不同地方的运输路径,发货方式,包装方式等不同,则收费标准也是不一样的。
类结构描述
- 发货货物类(基本属性)
- 发货抽象类(生成器)
- 各自不同地区的收费类(具体生成器,生成器的实现类)
- 发货的控制类(主管)
/// <summary> /// 物流公司发货基类 /// 运输方式、包装样式、运输类型 /// </summary> public class Matter { /// <summary> /// 包装样式 /// </summary> public string PackStyle{get;set;} /// <summary> /// 运输方式 /// </summary> public string TransportWay { get; set; } /// <summary> /// 是否有税费 /// </summary> public string IsHaveTax { get; set; } /// <summary> /// 总金额 /// </summary> /// <returns></returns> public double Money { get; set; } /// <summary> /// 描述信息 /// </summary> public string DescriptionInfo { get; set; } }
/// <summary> /// 发货实现类 :抽象建造者 /// </summary> public abstract class SendModelBuilder { protected Matter matter = new Matter(); public abstract void BuilderPackStyle(); public abstract void BuilderTransportWay(); public abstract void BuilderIsHaveTax(); public abstract void BuilderMoney(); public abstract void BuilderDescriptionInfo(); /// <summary> /// 返回一个完整得发货实现类 /// </summary> /// <returns></returns> public Matter CreateMatter() { return matter; } }
/// <summary> /// 日本地区创建者 /// </summary> public class RiBenBuilder : SendModelBuilder { public override void BuilderPackStyle() { matter.PackStyle = "木质箱子包装"; } public override void BuilderTransportWay() { matter.TransportWay = "水路"; } public override void BuilderIsHaveTax() { matter.IsHaveTax = "有"; } public override void BuilderMoney() { matter.Money = 1000; } public override void BuilderDescriptionInfo() { matter.DescriptionInfo = "此次运输为水路运输,货物为木质箱子包装,有税费,金额为1000元"; } }
/// <summary> /// 北京地区创建者 /// </summary> public class BeiJingBuilder : SendModelBuilder { public override void BuilderPackStyle() { matter.PackStyle = "纸盒"; } public override void BuilderTransportWay() { matter.TransportWay = "陆运"; } public override void BuilderIsHaveTax() { matter.IsHaveTax = "有"; } public override void BuilderMoney() { matter.Money = 50; } public override void BuilderDescriptionInfo() { matter.DescriptionInfo = "此次运输为陆运运输,货物为纸盒包装,无税费,金额为50元"; } }
/// <summary> /// 印度地区创建者 /// </summary> public class YinDuBuilder : SendModelBuilder { public override void BuilderPackStyle() { matter.PackStyle = "纸盒包装"; } public override void BuilderTransportWay() { matter.TransportWay = "空运"; } public override void BuilderIsHaveTax() { matter.IsHaveTax = "有"; } public override void BuilderMoney() { matter.Money = 3000; } public override void BuilderDescriptionInfo() { matter.DescriptionInfo = "此次运输为空运,货物为纸盒包装,有税费,金额为3000元"; } }
/// <summary> /// 急速物流发货创建控制器 /// </summary> public class JiSuWuLiuController { public Matter SendModel(SendModelBuilder builder) { builder.BuilderPackStyle(); builder.BuilderTransportWay(); builder.BuilderIsHaveTax(); builder.BuilderMoney(); builder.BuilderDescriptionInfo(); return builder.CreateMatter(); } }
上面罗列出的就是各个类的具体代码实现,下面是测试,目前以北京地区发货举例。
class Program { static void Main(string[] args) { SendModelBuilder smb = new BeiJingBuilder(); JiSuWuLiuController director = new JiSuWuLiuController(); Matter matter = director.SendModel(smb); Console.WriteLine("包装类型:"+matter.PackStyle); Console.WriteLine("运输方式:" + matter.TransportWay); Console.WriteLine("是否有税费:" + matter.IsHaveTax); Console.WriteLine("运费:" + matter.Money); Console.WriteLine("信息:"+matter.DescriptionInfo); Console.ReadKey(); } }
可以看到,我们在实现的时候,只是去声明并由directior来调用了BeiJingBuilder的创建者,具体的业务也只是单独在BeiJingBuilder类中去实现。如果我们想换成其余地区的发货,则只需要将声明换掉就可以。
小结
优点:
- 客户端不需要知道产品内部的组成细节,将产品本身与产品的创建进行分离,也就是解耦,使得相同的创建过程可以创建不同的产品对象。
- 具体构建着独立,增加新的建造者不需要修改原有库,系统扩展方便,符合开闭原则。
- 控制产品的创建过程很方便。
缺点:
- 当产品比较多时,导致代码比较冗余,系统变得过于庞大,增加系统的理解难度和运行成本。
- 此模式主要适用于当各自产品有一定共性的时候,如果产品独立性太强,无相同共性则不适合这个模式。