【設計模式】建造者模式 Builder Pattern

前面學習了簡單工廠模式工廠方法模式以及抽象工廠模式,這些都是建立類的對象所使用的一些經常使用的方法和套路, 那麼若是咱們建立一個很複雜的對象可上面的三種方法都不太適合,那麼「專業的事交給專業人去作」,23設計模式總有一個模式是適合這種複雜對象的建立。好比如今的智能手機組成, 它包括一個屏幕,攝像頭,耳機接口,USB接口,CPU, RAM,主板等等, 可是每個型號的手機的屏幕又不同,有的是劉海的,有的是全屏的,有的是全面屏的,CUP 也不同,有驍龍820 的,有 660的還有麒麟920 的等等,手機的組成圖以下:html

image_thumb4

image_thumb6

 

那麼要建立一個這樣的複雜對象, 該怎麼建立呢? 那麼該建造者模式閃亮登場了。編程

1、建造者模式定義

建造者模式(Builder Pattern):將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。建造者模式是一種對象建立型模式。設計模式

2、建造者模式結構圖

 image_thumb18

一、AbstractBuilder(抽象建造者):

它爲建立一個產品Product對象的各個部件指定抽象接口,在該接口中通常聲明兩類方法,一類方法是buildPartX(),它們用於建立複雜對象的各個部件;另外一類方法是GetResult(),它們用於返回複雜對象。AbstractBuilder既能夠是抽象類,也能夠是接口。app

二、ConcreteBuilder(具體建造者):

它實現了AbstractBuilder接口,實現各個部件的具體構造和裝配方法,定義並明確它所建立的複雜對象,也能夠提供一個方法返回建立好的複雜產品對象。ide

三、Product(產品角色):

它是被構建的複雜對象,包含多個組成部件,具體建造者建立該產品的內部表示並定義它的裝配過程。函數

四、Director(導演):

負責安排複雜對象的建造次序,導演與抽象建造者之間存在關聯關係,能夠在其Construct()建造方法中調用建造者對象的部件構造與裝配方法,完成複雜對象的建造。客戶端通常只須要與指揮者進行交互,在客戶端肯定具體建造者的類型,並實例化具體建造者對象(也能夠經過配置文件和反射機制),而後經過指揮者類的構造函數將該對象傳入指揮者類中。學習

3、建造者模式典型代碼結構

public class Product
{
    public string PartA { get; set; }
    public string PartB { get; set; }
    public string PartC { get; set; }
}

public abstract class AbstractBuilder
{
    protected Product product = new Product();
    public abstract void BuildPartA();
    public abstract void BuildPartB();
    public abstract void BuildPartC();
    public  Product GetResult()
    {          
        return product;
    }
}

public class ConreteBuilder : AbstractBuilder
{
    public override void BuildPartA()
    {
        this.product.PartA = "PartA";
    }

    public override void BuildPartB()
    {
        this.product.PartB = "PartB";
    }

    public override void BuildPartC()
    {
        this.product.PartC = "PartC";
    }   
}

public class Director
{
    private AbstractBuilder builder;
    public Director(AbstractBuilder builder)
    {
        this.builder = builder;
    }

    public Product Construct()
    {
        builder.BuildPartA();
        builder.BuildPartB();
        builder.BuildPartC();
        return builder.GetResult();
    }
}

客戶端調用:ui

static void Main(string[] args)
{
    AbstractBuilder builder = new ConreteBuilder();
    Director director = new Director(builder);
    Product product = director.Construct();

    Console.WriteLine(product.PartA);
    Console.WriteLine(product.PartB);
    Console.WriteLine(product.PartC);

    Console.ReadKey();
}

輸出結果:this

image_thumb12[4]

4、建造者模式實例

咱們用本文開頭提出的手組成的例子來挑選幾個核心部件來構造幾個型號的手機,使用建造者模式。spa

一、X1型手機:CUP 驍龍 835, RAM 6GB ,屏幕 劉海全屏, 硬盤 64GB。

二、X2 型手機: CUP 麒麟 930, RAM 8G, 屏幕 全面屏, 硬盤 128GB。

三、X3型手機: CPU  驍龍 960 RAM 10G,屏幕 超清全面屏 256GB。

UML 圖以下:

image_thumb34

代碼:

public class Mobile
{
    public string CPU { get; set; }
    public string Screen { get; set; }
    public string RAM { get; set; }
    public string Disk { get; set; }
}
public abstract class MobileBuilder
{
    protected Mobile mobile = new Mobile();
    public abstract void BuildCPU();
    public abstract void BuildScreen();
    public abstract void BuildRAM();
    public abstract void BuildDisk();
    public Mobile GetMobile()
    {
        return this.mobile;
    }
}

public class X1Builder:MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X1]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X1]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X1]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X1]:Disk had been built.";
    }
}

public class X2Builder:MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X2]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X2]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X2]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X2]:Disk had been built.";
    }
}

public class X3Builder:MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X3]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X3]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X3]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X3]:Disk had been built.";
    }
}

public class MobileDirector
{
    private MobileBuilder builder;
    public MobileDirector(MobileBuilder builder)
    {
      this.builder=builder;
    }

    public Mobile GetMobile(){
     this.builder.BuildCPU();
     this.builder.BuildRAM();
     this.builder.BuildScreen();
     this.builder.BuildDisk();
     return this.builder.GetMobile();
    }
}

調用代碼以下:

static void Main(string[] args)
{
    MobileBuilder builder=new X1Builder();
    MobileDirector mobileDrector = new MobileDirector(builder);
    Mobile mobile = mobileDrector.GetMobile();
    Console.WriteLine(mobile.CPU);
    Console.WriteLine(mobile.Screen);
    Console.WriteLine(mobile.RAM);
    Console.WriteLine(mobile.Disk);
    Console.ReadKey();
}

輸出結果:

image_thumb20 

若是想生產X2型號的手機,只須要將具體建造者代碼修改一下就能夠了,即將下面的一行代碼:

MobileBuilder builder=new X1Builder();

改爲:

MobileBuilder builder=new X2Builder();

輸出結果:

image_thumb22

也能夠將具體建造者類配置在配置文件中,經過反射來建立建造者對象進而建立出新的型號的手機。

在配置文件中加入以下配置:

<appSettings>
    <add key="Builder" value="DesignPattern.Builder.MobileInstance.X3Builder"/>
</appSettings>

客戶端調用代碼以下:

static void Main(string[] args)
{
    MobileBuilder builder;
    var setting = ConfigurationSettings.AppSettings["Builder"];
    var obj = Type.GetType(setting);
    if (obj == null) return;
    builder = Activator.CreateInstance(obj) as MobileBuilder;

    if (builder == null) return;
    
    MobileDirector mobileDrector = new MobileDirector(builder);
    Mobile mobile = mobileDrector.GetMobile();
    Console.WriteLine(mobile.CPU);
    Console.WriteLine(mobile.Screen);
    Console.WriteLine(mobile.RAM);
    Console.WriteLine(mobile.Disk);
    Console.ReadKey();
}

輸出結果:

image_thumb24

 

5、建造者模式的優勢

  1. 在建造者模式中,客戶端沒必要知道產品內部組成的細節,將產品自己與產品的建立過程解耦,使得相同的建立過程能夠建立不一樣的產品對象。
  2. 每個具體建造者都相對獨立,而與其餘的具體建造者無關,所以能夠很方便地替換具體建造者或增長新的具體建造者,用戶使用不一樣的具體建造者便可獲得不一樣的產品對象。因爲指揮者類針對抽象建造者編程,增長新的具體建造者無須修改原有類庫的代碼,系統擴展方便,符合「開閉原則(OCP)
  3. 能夠更加精細地控制產品的建立過程。將複雜產品的建立步驟分解在不一樣的方法中,使得建立過程更加清晰,也更方便使用程序來控制建立過程

6、建造者模式的缺點

  1. 建造者模式所建立的產品通常具備較多的共同點,其組成部分類似,若是產品之間的差別性很大,例如不少組成部分都不相同,不適合使用建造者模式,所以其使用範圍受到必定的限制。
  2. 若是產品的內部變化複雜,可能會致使須要定義不少具體建造者類來實現這種變化,致使系統變得很龐大,增長系統的理解難度和運行成本。

7、建造者模式的使用場景

  1. 須要生成的產品對象有複雜的內部結構,這些產品對象一般包含多個成員屬性。
  2. 須要生成的產品對象的屬性相互依賴,須要指定其生成順序。
  3. 對象的建立過程獨立於建立該對象的類。在建造者模式中經過引入了指揮者類,將建立過程封裝在指揮者類中,而不在建造者類和客戶類中。
  4. 隔離複雜對象的建立和使用,並使得相同的建立過程能夠建立不一樣的產品。

8、擴展 

Director在建造者模式中扮演着重要的角色,Director類看似簡單可是做用卻很是大,它決定複雜對象各個部分的建立順序而且將構建對象的過程和具體建造者隔離,好比建立一個房子,首先確定要作的事情是打地基,而後是磊牆,而後是封頂,最後是裝修,這個過程是有順序的且必須是這個順序,其它過程不沒法完成房子的建造。

Director'就好像是拍電影導演同樣,導演要拍一部電影,須要拍攝若干影像片斷,最後由剪輯師作剪接拼接,導演最後會進行最終的剪輯排版合成一個長片-- 電影。那麼電影就充當了建造者模式中的複雜產品,拍攝的影像片斷就是產片的各個部分,剪輯就是建造者模式的具體具體建造者,劇本是建造者模式的抽象建造者,導演就是Director。那麼在在這個過程當中,編劇是不能夠兼任導演? 剪輯師是否是也能夠兼任導演呢?答案是確定的。

1.抽象建造者幹了Director的活

那上面手機建造的實例,刪除掉Diretor, 將建立複雜對象的邏輯放到抽象建造者中,而且使子類方法不能重寫父類中的建造產品的方法,引用以前的配置代碼演變成以下:

public class Mobile
{
    public string CPU { get; set; }
    public string Screen { get; set; }
    public string RAM { get; set; }
    public string Disk { get; set; }
}
public abstract class MobileBuilder
{
    protected Mobile mobile = new Mobile();
    public abstract void BuildCPU();
    public abstract void BuildScreen();
    public abstract void BuildRAM();
    public abstract void BuildDisk();
    public static Mobile GetMobile(MobileBuilder builder)
    {
        builder.BuildCPU();
        builder.BuildScreen();
        builder.BuildRAM();
        builder.BuildDisk();
        return builder.mobile;
    }
}

public class X1Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X1]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X1]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X1]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X1]:Disk had been built.";
    }
}

public class X2Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X2]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X2]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X2]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X2]:Disk had been built.";
    }
}

public class X3Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X3]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X3]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X3]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X3]:Disk had been built.";
    }
}

調用代碼:

static void Main(string[] args)
{
    MobileBuilder builder;
    var setting = ConfigurationSettings.AppSettings["Builder"];
    var obj = Type.GetType(setting);
    if (obj == null) return;
    builder = Activator.CreateInstance(obj) as MobileBuilder;

    if (builder == null) return;

    Mobile mobile = builder.GetMobile();
    Console.WriteLine(mobile.CPU);
    Console.WriteLine(mobile.Screen);
    Console.WriteLine(mobile.RAM);
    Console.WriteLine(mobile.Disk);
    Console.ReadKey();
}

輸出結果:

image_thumb26

這種在抽象建造者中使用了一個靜態方法來建立產品的作法的好處是產品建立出來的一致性很好,建立產品流程被統一封裝,通常不會有差別,這種方式抽象類控制了產品建造的順序,而且全部的產品的建立順序都不能改變了(如造房子的流程),對於要求建立順序一致,而且產品部件的建立都路程一致的產品來講這是一個優勢。

可是若是想建立出來的產品有差別,每個產品的順序都不同那該怎麼辦呢?好比如今這種方法建立出來的順序是: CPU=》Screen=》RAM=》Disk, 那麼我要想X3型號的手機建立順序變成:CPU=》RAM=》Disk=》Screen。 這種方法就顯得不靈活了,沒有辦法作到個性化了。處理典型的將建造過程的控制權交給Director外,還能夠不用Director來完成嗎?

二、具體建造者和抽象建造者均可以幹Director的活

這裏咱們依然刪掉Director, 將抽象方法中的建立方法變成子類能夠重寫的虛方法就能夠了,而後在具體建造者中重寫抽象建造者的建立方法就能夠了。

如今咱們建立X1,X2,X3型號的手機的順序分別是這樣的:

X1: CPU=》Screen=》RAM=》Disk

X2:CPU=》RAM=》Disk=》Screen

X3:CPU=》Disk=》RAM=》Screen

代碼以下:

public class Mobile
{
    public string CPU { get; set; }
    public string Screen { get; set; }
    public string RAM { get; set; }
    public string Disk { get; set; }
}
public abstract class MobileBuilder
{
    protected Mobile mobile = new Mobile();
    public abstract void BuildCPU();
    public abstract void BuildScreen();
    public abstract void BuildRAM();
    public abstract void BuildDisk();
    public virtual Mobile GetMobile()
    {
        this.BuildCPU();
        this.BuildScreen();
        this.BuildRAM();
        this.BuildDisk();
        Console.WriteLine(mobile.CPU);
        Console.WriteLine(mobile.Screen);
        Console.WriteLine(mobile.RAM);
        Console.WriteLine(mobile.Disk);
       
        return this.mobile;
    }
}

public class X1Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X1]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X1]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X1]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X1]:Disk had been built.";
    }
}

public class X2Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X2]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X2]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X2]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X2]:Disk had been built.";
    }

    public override Mobile GetMobile()
    {
        BuildCPU();
        BuildRAM();
        BuildDisk();
        BuildScreen();
        Console.WriteLine(mobile.CPU);       
        Console.WriteLine(mobile.RAM);
        Console.WriteLine(mobile.Disk);
        Console.WriteLine(mobile.Screen);
        return this.mobile;
    }
}

public class X3Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X3]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X3]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X3]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X3]:Disk had been built.";
    }
    public override Mobile GetMobile()
    {
        BuildCPU();        
        BuildDisk();
        BuildRAM();
        BuildScreen();
        Console.WriteLine(mobile.CPU);
        Console.WriteLine(mobile.Disk);
        Console.WriteLine(mobile.RAM);       
        Console.WriteLine(mobile.Screen);
        return this.mobile;
    }
}

調用代碼:

static void Main(string[] args)
{
    MobileBuilder builder;
    var setting = ConfigurationSettings.AppSettings["Builder"];
    var obj = Type.GetType(setting);
    if (obj == null) return;
    builder = Activator.CreateInstance(obj) as MobileBuilder;

    if (builder == null) return;

    Mobile mobile = builder.GetMobile();

    Console.ReadKey();
}

輸出結果:

image_thumb28

3.  控制某些部件不用被建立

假如要造一個X4型號的手機,這個手機支持NFC,咱們知道X1,X2,X3中都不支持NFC,那怎麼辦呢?,咱們能夠給抽象建造者類加一個方法,叫 HasNFC()而且返回bool值,並將其設置成默認值爲false。 修改抽象建造者的GetMobile() 方法,只有當HasNFC()返回 true是才建立NFC模塊,而且在X4Builder的具體建造者類中重寫HasNFC()方法使其返回true就能夠了。

代碼以下:

public class Mobile
{
    public string CPU { get; set; }
    public string Screen { get; set; }
    public string RAM { get; set; }
    public string Disk { get; set; }
}
public abstract class MobileBuilder
{
    protected Mobile mobile = new Mobile();
    public abstract void BuildCPU();
    public abstract void BuildScreen();
    public abstract void BuildRAM();
    public abstract void BuildDisk();
    public virtual void BuildNFC()
    {
        Console.WriteLine("NFC had been built.");
    }
    protected virtual bool HasNFC()
    {
        return false;
    }
    public virtual Mobile GetMobile()
    {
        this.BuildCPU();
        this.BuildScreen();
        this.BuildRAM();
        this.BuildDisk();
        if(HasNFC())
        {
            this.BuildNFC();
        }
        Console.WriteLine(mobile.CPU);
        Console.WriteLine(mobile.Screen);
        Console.WriteLine(mobile.RAM);
        Console.WriteLine(mobile.Disk);
       
        return this.mobile;
    }
}

public class X1Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X1]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X1]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X1]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X1]:Disk had been built.";
    }
}

public class X2Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X2]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X2]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X2]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X2]:Disk had been built.";
    }

    public override Mobile GetMobile()
    {
        BuildCPU();
        BuildRAM();
        BuildDisk();
        BuildScreen();
        Console.WriteLine(mobile.CPU);       
        Console.WriteLine(mobile.RAM);
        Console.WriteLine(mobile.Disk);
        Console.WriteLine(mobile.Screen);
        return this.mobile;
    }
}

public class X3Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X3]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X3]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X3]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X3]:Disk had been built.";
    }
    public override Mobile GetMobile()
    {
        BuildCPU();        
        BuildDisk();
        BuildRAM();
        BuildScreen();
        Console.WriteLine(mobile.CPU);
        Console.WriteLine(mobile.Disk);
        Console.WriteLine(mobile.RAM);       
        Console.WriteLine(mobile.Screen);
        return this.mobile;
    }
}
public class X4Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X4]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X4]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X4]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X4]:Disk had been built.";
    }
    
    protected override bool HasNFC()
    {
 	     return true;
    }
}

App.Config 配置:

<appSettings>
    <add key="Builder" value="DesignPattern.Builder.MobileInstance.X3Builder"/>
</appSettings>

客戶端代碼:

static void Main(string[] args)
{
    MobileBuilder builder;
    
    var setting = ConfigurationSettings.AppSettings["Builder"];
    var obj = Type.GetType(setting);
    if (obj == null) return;
    builder = Activator.CreateInstance(obj) as MobileBuilder;

    if (builder == null) return;

    Mobile mobile = builder.GetMobile();

    Console.ReadKey();
}

輸出結果:

image_thumb30

修改配置文件,使其造一臺X4 以下,調用代碼不變:

<appSettings>
    <add key="Builder" value="DesignPattern.Builder.MobileInstance.X4Builder"/>
</appSettings>

輸出結果:

image_thumb32

好了建造者模式就探討到這裏。

相關文章
相關標籤/搜索