Builder模式,中文名为建造者模式,又名生成器模式、构建者模式等,是建立型设计模式之一。用于将一个复杂对象的构建与它的表示分离,使得一样的构建过程能够建立不一样的表示。git
Builder
:定义建立Product
各个部件的抽象接口ConcreteBuilder
:继承自Builder
,实现建立具体类型的Product
全部部件的接口,并提供一个获取最终产品的接口。Director
:借助Builder
,内部封装建立产品的具体流程。想象一下,你做为造物主,你须要创造各类各样的动物,好比狗和猫。查看源码
1.在创造前,要先想好狗和猫大致是什么样子,很显然,它们应该有头、身体和脚。算法
abstract class Animal { public string Head { get; set; } public string Body { get; set; } public string Foots { get; set; } public abstract void Display(); } class Dog : Animal { public override void Display() { Console.WriteLine("-------------- 狗的基本信息 -------------------"); Console.WriteLine($"头:{Head}"); Console.WriteLine($"身体:{Body}"); Console.WriteLine($"脚:{Foots}"); } } class Cat : Animal { public override void Display() { Console.WriteLine("-------------- 猫的基本信息 -------------------"); Console.WriteLine($"头:{Head}"); Console.WriteLine($"身体:{Body}"); Console.WriteLine($"脚:{Foots}"); } }
2.动物的基本特征肯定了,也就明确了要干什么活儿了——建立动物的头、身体和脚。设计模式
interface IAnimalBuilder { void SetHead(); void SetBody(); void SetFoots(); }
3.接下来就是考虑狗和猫各部位的建立细节了。不过,别着急编码,先来考虑一件事情:
DogBuilder
里面除了须要实现IAnimalBuilder
的全部接口外,还须要提供一个GetDog()
的方法,用来把建立好的Dog
给外部;一样的CatBuilder
中也须要提供一个GetCat()
方法。这世间动物有多少种?一千?一万?远远不止!在编码的过程当中很容易就忘记提供这个方法了。因此咱们在抽象Builder和具体Builder中间插入一个抽象层IAnimalBuilder<TAnimal>
,全部的ConcreteBuilder都继承自这个接口。框架
interface IAnimalBuilder<TAnimal> : IAnimalBuilder where TAnimal : Animal { TAnimal GetAnimal(); } class DogBuilder : IAnimalBuilder<Dog> { private readonly Dog _dog = new Dog(); public void SetHead() { _dog.Head = "狗的头"; } public void SetBody() { _dog.Body = "狗的身体"; } public void SetFoots() { _dog.Foots = "狗的脚"; } public Dog GetAnimal() { return _dog; } } class CatBuilder : IAnimalBuilder<Cat> { private readonly Cat _cat = new Cat(); public void SetHead() { _cat.Head = "猫的头"; } public void SetBody() { _cat.Body = "猫的身体"; } public void SetFoots() { _cat.Foots = "猫的脚"; } public Cat GetAnimal() { return _cat; } }
4.最后,只剩Director
了,它来规划Builder要建立哪些东西。ide
class Director { private readonly IAnimalBuilder _builder; public Director(IAnimalBuilder builder) { _builder = builder; } public void Construct() { _builder.SetHead(); _builder.SetBody(); _builder.SetFoots(); } }
大功告成!接下来就尝试一下吧测试
var dogBuilder = new DogBuilder(); var dogDirector = new Director(dogBuilder); dogDirector.Construct(); dogBuilder.GetAnimal().Display(); var catBuilder = new CatBuilder(); var catDirector = new Director(catBuilder); catDirector.Construct(); catBuilder.GetAnimal().Display();
实际开发过程当中,常常会遇到一种需求:建立对象时,对象的一些部分能够自由选择设置或不设置,可是对象一旦建立完成,便不能对其进行任何修改。例如:ASP.NET Core
框架中WebHost
的建立。接下来,咱们来模拟一下女娲造人。
1.一样的,咱们先设定好人的基本属性ui
class Person { public string Name { get; set; } public int Gender { get; set; } public int Age { get; set; } }
2.接下来,咱们来定义IPersonBuilder
接口,除了Build
方法外,咱们都返回了IPersonBuilder
,对于习惯于使用Linq
的.Net开发人员来讲,再熟悉不过了,它最大的好处就是可以让咱们使用Fluent
的方式来编写代码。this
public interface IPersonBuilder { //注意,这里有问题 Person Build(); IPersonBuilder SetName(string name); IPersonBuilder SetGender(int gender); IPersonBuilder SetAge(int age); }
你发现什么问题了吗?没错,这违背了面向对象五大原则之一——依赖倒置(抽象不该该依赖具体);并且,这也不知足“对象建立后不得进行更改”的需求。因此,咱们须要提取出一个抽象层IPersion
。编码
public interface IPerson { string Name { get; } int Gender { get; } int Age { get; } } class Person : IPerson { public string Name { get; set; } public int Gender { get; set; } public int Age { get; set; } } public interface IPersonBuilder { IPerson Build(); IPersonBuilder SetName(string name); IPersonBuilder SetGender(int gender); IPersonBuilder SetAge(int age); }
3.下一步就是构建PersonBuilder
了,用来实现IPersonBuilder
接口的具体逻辑。设计
class PersonBuilder : IPersonBuilder { private readonly Person _person = new Person(); public IPerson Build() { return _person; } public IPersonBuilder SetName(string name) { _person.Name = name; return this; } public IPersonBuilder SetGender(int gender) { _person.Gender = gender; return this; } public IPersonBuilder SetAge(int age) { _person.Age = age; return this; } } //提供一个辅助类,用来帮 Client 建立 PersonBuilder public class PersonBuilderHelper { public static IPersonBuilder CreatePersonBuilder() { return new PersonBuilder(); } }
4.Ok,大功告成!来测试一下吧
var person = PersonBuilderHelper.CreatePersonBuilder() .SetAge(20) .SetName("jjj") .Build(); //输出:jjj,0,20 Console.WriteLine($"{person.Name},{person.Gender},{person.Age}");
经典版与变种版本的区别:
查看源码 若是有新发现会进行补充!