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}");
經典版與變種版本的區別:
查看源碼 若是有新發現會進行補充!