1. 程式人生 > >C#.Net 設計模式學習筆記之創建型 (一)

C#.Net 設計模式學習筆記之創建型 (一)

應用 種類 單件 src nag abstract 子類 指定 相關

1、抽象工廠(Abstract Factory)模式

常規的對象創建方法:

//創建一個Road對象
Road road =new Road();


new 的問題:
實現依賴,不能應對“具體實例化類型”的變化。
解決思路:
封裝變化點-----哪裏變化,封裝哪裏
潛臺詞: 如果沒有變化,當然不需要額外的封裝!

工廠模式的緣起
變化點在“對象創建”,因此就封裝“對象創建”
面向接口編程----依賴接口,而非依賴實現
最簡單的解決方法:

class RoadFactory

{
  public static Road CreateRoad()
  {
  return new Road();
  }
}
//創建一個Road對象
Road road=roadFactory.CreateRoad();

創建一系列相互依賴對象的創建工作:
假設一個遊戲開場景:
我們需要構造"道路"、"房屋"、"地道","從林"...等等對象
工廠方法如下:

class RoadFactory
{
public static Road CreateRoad()
{
return new Road();
}
public static Building CreateBuilding()
{
return new Building();
}
public static Tunnel CreateTunnel()
{
return new Tunnel();
}
public static Jungle CreateJungle()
{
return new Jungle();
}
}

調用方式如下:

Road road = RoadFactory.CreateRoad();

Building building = RoadFactory.CreateBuilding();
Tunnel tunnel = RoadFactory.CreateTunnel();
Jungle jungle = RoadFactory.CreateJungle();

如上可見簡單工廠的問題:
不能應對"不同系列對象"的變化。比如有不同風格的場景---對應不同風格的道路,房屋、地道....

如何解決:
使用面向對象的技術來"封裝"變化點。
動機(Motivate):


在軟件系統中,經常面臨著"一系統相互依賴的對象"的創建工作:同時,由於需求的變化,往往存在更多系列對象的創建工作。
如何應對這種變化?如何繞過常規的對象創建方法(new),提供一種"封裝機制"來避免客戶程序和這種"多系列具體對象創建工作"的緊耦合?

意圖(Intent):
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

----摘自《設計模式》

結構圖(Struct):

技術分享

適用性:
1.一個系統要獨立於它的產品的創建、組合和表示時。
2.一個系統要由多個產品系統中的一個來配置時。
3.當你要強調一系列相關的產品對象的設計以便進行聯合使用時。
4.當你提供一個產品類庫,而只想顯示它們的接口不是實現時。
生活例子:

技術分享

結構圖代碼實現:

abstract class AbstractFactory
{
public abstract AbstractProductA CreateProductA();
public abstract AbstractProductB CreateProductB();
}

abstract class AbstractProductA
{
public abstract void Interact(AbstractProductB b);
}

abstract class AbstractProductB
{
public abstract void Interact(AbstractProductA a);
}

class Client
{
private AbstractProductA AbstractProductA;
private AbstractProductB AbstractProductB;
public Client(AbstractFactory factory)
{
AbstractProductA = factory.CreateProductA();
AbstractProductB = factory.CreateProductB();
}
public void Run()
{
AbstractProductB.Interact(AbstractProductA);
AbstractProductA.Interact(AbstractProductB);
}
}

class ConcreteFactory1:AbstractFactory
{
public override AbstractProductA CreateProductA()
{
return new ProductA1();
}
public override AbstractProductB CreateProductB()
{
return new ProductB1();
}
}

class ConcreteFactory2:AbstractFactory
{
public override AbstractProductA CreateProductA()
{
return new ProdcutA2();
}
public override AbstractProductB CreateProductB()
{
return new ProductB2();
}
}

class ProductA1:AbstractProductA
{
public override void Interact(AbstractProductB b)
{
Console.WriteLine(this.GetType().Name + "interact with" + b.GetType().Name);
}
}

class ProductB1:AbstractProductB
{
public override void Interact(AbstractProductA a)
{
Console.WriteLine(this.GetType().Name + "interact with" + a.GetType().Name);
}
}

class ProdcutA2:AbstractProductA
{
public override void Interact(AbstractProductB b)
{
Console.WriteLine(this.GetType().Name + "interact with" + b.GetType().Name);
}
}

class ProductB2:AbstractProductB
{
public override void Interact(AbstractProductA a)
{
Console.WriteLine(this.GetType().Name + "interact with" + a.GetType().Name);
}
}

public static void Main()
{
// Abstractfactory1
AbstractFactory factory1 = new ConcreteFactory1();
Client c1 = new Client(factory1);
c1.Run();
// Abstractfactory2
AbstractFactory factory2 = new ConcreteFactory2();
Client c2 = new Client(factory2);
c2.Run();
}

Abstract Factory註意的幾點:
如果不存在”多系列對象創建“的需求變化,則沒必要應用Abstract Factory模式,靜態工廠方法足矣。
"系列對象"指的是這些對象之間有相互依賴、或作用的關系。例如遊戲開發場景中的"道路"與"房屋"依賴,“道路”與“地道”的依賴。
Abstract Factory模式主要在於應對"新系列"的需求變動。其缺點在於難以應對”新對象“的需求變動。
Abstract Factory模式經常和Factory Method模式共同組合來應對“對象創建”的需求變化。

2、單件模式(Singleto Pattern)

動機(Motivation):
在軟件系統中,經常有這樣一些特殊的類,必須保證它們在系統中只存在一個實例,才能確保它們的邏輯正確性、以及良好的效率。
如何繞過常規的構造器,提供一種機制來保證一個類只創建一個實例?
這應該是類設計者的責任,而不是類使用者的責任。
結構圖:

技術分享

意圖:
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

----摘自《設計模式》

生活的例子:

技術分享

適用性:
(1)當類只能有一個實例而且客戶可以從一個眾所周知的訪問點訪問它時。
(2)當這個唯一實例應該是通過子類化可擴展的,並且客戶應該無需更改代碼就能使用一個擴展的實例時。

代碼實現:
1)單線程Singleton實現

class SingleThread_Singleton
{
private static SingleThread_Singleton instance = null;
private SingleThread_Singleton(){}
public static SingleThread_Singleton Instance
{
get
{
if (instance == null)
{
instance = new SingleThread_Singleton();
}
return instance;
}
}
}


以上代碼在單線程情況下不會出現任何問題。但是在多線程的情況下卻不是安全的。
如兩個線程同時運行到 if (instance == null)判斷是否被實例化,一個線程判斷為True後,在進行創建
instance = new SingleThread_Singleton();之前,另一個線程也判斷(instance == null),結果也為True.
這樣就就違背了Singleton模式的原則(保證一個類僅有一個實例)。
怎樣在多線程情況下實現Singleton?
2)多線程Singleton實現:

class MultiThread_Singleton
{
private static volatile MultiThread_Singleton instance = null;
private static object lockHelper = new object();
private MultiThread_Singleton() { }
public static MultiThread_Singleton Instance
{
get
{
if (instance == null)
{
lock (lockHelper)
{
if (instance == null)
{
instance = new MultiThread_Singleton();
}
}
}
return instance;
}
}

此程序對多線程是安全的,使用了一個輔助對象lockHelper,保證只有一個線程創建實例(如果instance為空,保證只有一個線程instance = new MultiThread_Singleton();創建唯一的一個實例)。(Double Check)
請註意一個關鍵字volatile,如果去掉這個關鍵字,還是有可能發生線程不是安全的。
volatile 保證嚴格意義的多線程編譯器在代碼編譯時對指令不進行微調。
(3)靜態Singleton實現


class Static_Singleton
{
public static readonly Static_Singleton instance = new Static_Singleton();
private Static_Singleton() { }
}


以上代碼展開等同於

class Static_Singleton
{
public static readonly Static_Singleton instance;
static Static_Singleton()
{
instance = new Static_Singleton();
}
private Static_Singleton() { }
}

由此可以看出,完全符合Singleton的原則。
優點: 簡潔,易懂
缺點: 不可以實現帶參數實例的創建。
(註:以上代碼及信息借鑒於李建忠老師的MSDN和TerryLee的文章。)

3、工廠方法模式(Factory Method)

耦合關系:

耦合關系直接決定著軟件面對變化時的行為

--模塊與模塊之間的緊耦合使得軟件面對變化時,相關的模塊都要隨之更改;

--模塊與模塊之間的松耦合使得軟件面對變化時,一些模塊更容易被替換或者更改,但其他模塊保持不變。

動機(Motivation):
在軟件系統中,由於需求的變化,"這個對象的具體實現"經常面臨著劇烈的變化,但它卻有比較穩定的接口。
如何應對這種變化呢?提供一種封裝機制來隔離出"這個易變對象"的變化,從而保持系統中"其它依賴的對象"不隨需求的變化而變化。
意圖(Intent):
定義一個用戶創建對象的接口,讓子類決定實例哪一個類。Factory Method使一個類的實例化延遲到子類。

----摘自《設計模式》

結構圖(Struct):

技術分享

生活實例:

技術分享

適用性:
1.當一個類不知道它所必須創建的對象類的時候。
2.當一個類希望由它子類來指定它所創建對象的時候。
3.當類將創建對象的職責委托給多個幫助子類中的某個,並且你希望將哪一個幫助子類是代理者這一信息局部化的時候。

實例代碼:
CarFactory類:

public abstract class CarFactory
{
public abstract Car CarCreate();
}

Car類:

public abstract class Car
{
public abstract void StartUp();
public abstract void Run();
public abstract void Stop();

}

HongQiCarFactory類:

public class HongQiCarFactory:CarFactory
{
public override Car CarCreate()
{
return new HongQiCar();
}
}

BMWCarFactory類:

public class BMWCarFactory:CarFactory
{
public override Car CarCreate()
{
return new BMWCar();
}
}

HongQiCar類:

public class HongQiCar:Car
{
public override void StartUp()
{
Console.WriteLine("Test HongQiCar start-up speed!");
}
public override void Run()
{
Console.WriteLine("The HongQiCar run is very quickly!");
}
public override void Stop()
{
Console.WriteLine("The slow stop time is 3 second ");
}
}

BMWCar類:

public class BMWCar:Car
{
public override void StartUp()
{
Console.WriteLine("The BMWCar start-up speed is very quickly");
}
public override void Run()
{
Console.WriteLine("The BMWCar run is quitely fast and safe!!!");
}
public override void Stop()
{
Console.WriteLine("The slow stop time is 2 second");
}
}

app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="No1" value="HongQiCarFactory"/>
<add key="No2" value="BMWCarFactory"/>
</appSettings>
</configuration>

Program類:

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please Enter Factory Method No:");
Console.WriteLine("******************************");
Console.WriteLine("no Factory Method");
Console.WriteLine("1 HongQiCarFactory");
Console.WriteLine("2 BMWCarFactory");
Console.WriteLine("******************************");
int no=Int32.Parse(Console.ReadLine().ToString());
string factoryType=ConfigurationManager.AppSettings["No"+no];
//CarFactory factory = new HongQiCarFactory();
CarFactory factory = (CarFactory)Assembly.Load("FactoryMehtod").CreateInstance("FactoryMehtod." + factoryType); ;
Car car=factory.CarCreate();
car.StartUp();
car.Run();
car.Stop();

}
}

Factory Method 模式的幾個要點:
Factory Method模式主要用於隔離類對象的使用者和具體類型之間的耦合關系。面對一個經常變化的具體類型,緊耦合關系會導致軟件的脆弱。
Factory Method模式通過面向對象的手法,將所要創建的具體對象工作延遲到子類,從而實現一種擴展(而非更改)的策略,較好地解決了這種緊耦合關系。
Factory Mehtod模式解決"單個對象"的需求變化,AbstractFactory模式解決"系列對象"的需求變化,Builder模式解決"對象部分"的需求變化。

4、建造者模式(Builder)

  假設創建遊戲中的一個房屋House設施,該房屋的構建由幾部分組成,且各個部分富於變化。

如果使用最直觀的設計方法,每一個房屋部分的變化,都將導致房屋構建的重新修正.....

動機(Motivation):
在軟件系統中,有時候面臨一個"復雜對象"的創建工作,其通常由各個部分的子對象用一定算法構成;由於需求的變化,這個復雜對象的各個部分經常面臨著劇烈的變化,但是將它們組合到一起的算法卻相對穩定。
如何應對種變化呢?如何提供一種"封裝機制"來隔離出"復雜對象的各個部分"的變化,從而保持系統中的"穩定構建算法"不隨需求的改變而改變?
意圖(Intent):
將一個復雜對象的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。

----摘自《設計模式》

結構圖(Struct):

技術分享

協作(Collaborations):

技術分享

生活中的例子:

技術分享

適用性:
1.當創建復雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時。
2.當構造過程必須允許被構造的對象有不同的表示時。
實例代碼:
Builder類:

public abstract class Builder
{
public abstract void BuildDoor();
public abstract void BuildWall();
public abstract void BuildWindows();
public abstract void BuildFloor();
public abstract void BuildHouseCeiling();

public abstract House GetHouse();
}

Director類:這一部分是 組合到一起的算法(相對穩定)。

public class Director
{
public void Construct(Builder builder)
{
builder.BuildWall();
builder.BuildHouseCeiling();
builder.BuildDoor();
builder.BuildWindows();
builder.BuildFloor();
}
}

ChineseBuilder類

public class ChineseBuilder:Builder
{
private House ChineseHouse = new House();
public override void BuildDoor()
{
Console.WriteLine("this Door ‘s style of Chinese");
}
public override void BuildWall()
{
Console.WriteLine("this Wall ‘s style of Chinese");
}
public override void BuildWindows()
{
Console.WriteLine("this Windows ‘s style of Chinese");
}
public override void BuildFloor()
{
Console.WriteLine("this Floor ‘s style of Chinese");
}
public override void BuildHouseCeiling()
{
Console.WriteLine("this Ceiling ‘s style of Chinese");
}
public override House GetHouse()
{
return ChineseHouse;
}
}

RomanBuilder類:

class RomanBuilder:Builder
{
private House RomanHouse = new House();
public override void BuildDoor()
{
Console.WriteLine("this Door ‘s style of Roman");
}
public override void BuildWall()
{
Console.WriteLine("this Wall ‘s style of Roman");
}
public override void BuildWindows()
{
Console.WriteLine("this Windows ‘s style of Roman");
}
public override void BuildFloor()
{
Console.WriteLine("this Floor ‘s style of Roman");
}
public override void BuildHouseCeiling()
{
Console.WriteLine("this Ceiling ‘s style of Roman");
}
public override House GetHouse()
{
return RomanHouse;
}
}

ChineseBuilder和RomanBuilder這兩個是:這個復雜對象的兩個部分經常面臨著劇烈的變化。

public class Client
{
public static void Main(string[] args)
{
Director director = new Director();

Builder instance;

Console.WriteLine("Please Enter House No:");

string No = Console.ReadLine();

string houseType = ConfigurationSettings.AppSettings["No" + No];

instance = (Builder)Assembly.Load("House").CreateInstance("House." + houseType);

director.Construct(instance);

House house= instance.GetHouse();
house.Show();

Console.ReadLine();
}
}

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="No1" value="RomanBuilder"></add>
<add key="No2" value="ChineseBuilder"></add>
</appSettings>
</configuration>


Builder模式的幾個要點:

Builder模式 主要用於“分步驟構建一個復雜的對象”。在這其中“分步驟”是一個穩定的乘法,而復雜對象的各個部分則經常變化。

Builder模式主要在於應對“復雜對象各個部分”的頻繁需求變動。其缺點在於難以應對“分步驟構建算法”的需求變動。

Abstract Factory模式解決“系列對象”的需求變化,Builder模式解決“對象部分”的需求變化。Builder械通常和Composite模式組合使用。

5、原型模式(Prototype)

依賴關系倒置:

  抽象不應該依賴於 實現細節,實現細節應該依賴於抽象。

動機(Motivate):
在軟件系統中,經常面臨著“某些結構復雜的對象”的創建工作;由於需求的變化,這些對象經常面臨著
劇烈的變化,但是它們卻擁有比較穩定一致的接口。
如何應對這種變化?如何向“客戶程序(使用這些對象的程序)"隔離出“這些易變對象”,從而使得“依賴這些易變對象的客戶程序”不隨著需求改變而改變?
意圖(Intent):
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。

----摘自《設計模式》

結構圖(Struct):

技術分享

生活例子:

技術分享

適用性:

1.當一個系統應該獨立於它的產品創建,構成和表示時;

2.當要實例化的類是在運行時刻指定時,例如,通過動態裝載;

3.為了避免創建一個與產品類層次平行的工廠類層次時;

4.當一個類的實例只能有幾個不同狀態組合中的一種時。建立相應數目的原型並克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。

示意性代碼例子:

public abstract class NormalActor
{
public abstract NormalActor clone();
}

public class NormalActorA:NormalActor
{
public override NormalActor clone()
{
Console.WriteLine("NormalActorA is call");
return (NormalActor)this.MemberwiseClone();

}
}

public class NormalActorB :NormalActor
{
public override NormalActor clone()
{
Console.WriteLine("NormalActorB was called");
return (NormalActor)this.MemberwiseClone();

}
}


public class GameSystem
{
public void Run(NormalActor normalActor)
{
NormalActor normalActor1 = normalActor.clone();
NormalActor normalActor2 = normalActor.clone();
NormalActor normalActor3 = normalActor.clone();
NormalActor normalActor4 = normalActor.clone();
NormalActor normalActor5 = normalActor.clone();

}
}

class Program
{
static void Main(string[] args)
{
GameSystem gameSystem = new GameSystem();

gameSystem.Run(new NormalActorA());
}
}

如果又需要創建新的對象(flyActor),只需創建此抽象類,然後具體類進行克隆。

public abstract class FlyActor
{
public abstract FlyActor clone();
}

public class FlyActorB:FlyActor
{
/// <summary>
/// 淺拷貝,如果用深拷貝,可使用序列化
/// </summary>
/// <returns></returns>
public override FlyActor clone()
{
return (FlyActor)this.MemberwiseClone();
}
}
此時,調用的Main()函數只需如下:
class Program
{
static void Main(string[] args)
{
GameSystem gameSystem = new GameSystem();

gameSystem.Run(new NormalActorA(), new FlyActorB());
}
}

Prototype的幾個要點:

Prototype模式同樣用於隔離類對象的使用者和具體類型(易變類)之間的耦合關系,它同樣要求這
些“易變類”擁有“穩定的接口”。
Prototype模式對於“如何創建易變類的實體對象“采用“原型克隆”的方法來做,它使得我們可以
非常靈活地動態創建“擁有某些穩定接口中”的新對象----所需工作僅僅是註冊的地方不斷地Clone.
Prototype模式中的Clone方法可以利用.net中的object類的memberwiseClone()方法或者序列化來實現深拷貝。
有關創建型模式的討論:
Singleton模式解決的是實體對象個數的問題。除了Singleton之外,其他創建型模式解決的是都是new 所帶來的耦合關系。
Factory Method ,Abstract Factory,Builder都需要一個額外的工廠類來負責實例化“易變對象”,而Prototype則是通過原型(一個特殊的工廠類)來克隆“易變對象”。
如果遇到“易變類”,起初的設計通常從Factory Mehtod開始,當遇到更多的復雜變化時,再考慮重重構為其他三種工廠模式(Abstract Factory,Builder,Prototype).

C#.Net 設計模式學習筆記之創建型 (一)