《大話設計模式》——學習筆記之"創建型模式"(單例&工廠方法&抽象工廠&建造者&原型)

《大話設計模式》——學習筆記之”創建型模式”(單例&工廠方法&抽象工廠&建造者&原型)

單例模式

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

<da_021>

Singleton類,定義一個GetInstance操作,允許客戶訪問它的唯一實例,GetInstance是一個靜態方法,主要負責創建自己的唯一實例

class Singleton{
    private static Singleton instance;
    private Singleton(){

    }

    public static Singleton GetInstance(){
        if(instance == null){
            //若實例不存在,則new一個新實例,否則返回已有的實例
            instance = new Singleton();
        }
        return instance;
    }
}

優點:

  • 保證一個類僅有一個實例,並提供一個訪問它的全局訪問點
  • 可以嚴格地控制客戶怎樣訪問以及何時訪問實例

工廠方法模式

定義: 定義一個用於創建對象的接口,讓子類決定實例化哪一個類,工廠方法使一個類的實例化延遲到其子類

<img da_009>

先構建一個工廠接口

interface IFactory{
    Operation CreateOperation();
}

構建實現工廠接口的工廠類

class AddFactory : IFactory{
    public Operation CreateOperation(){
        return new OperationAdd();
    }
}

class SubFactory : IFactory{
    public Operation CreateOperation(){
        return new OperationSub();
    }
}
...

客戶端實現

IFactory operFactory = new AddFactory();
Operation oper = operFactory.CreateOperation();
oper.NumberA = 1;
oper.NumberB = 2;
double result = oper.GetResult();

優點:

  • 定義一個用於創建對象的接口,讓子類決定實例化哪一個類,工廠模式使一個類的實例化延遲到其子類
  • 在創建對象時,使用抽象工廠、原型、建造者的設計比使用工廠方法要更靈活,但它們也更加複雜,通常,設計是以使用工廠方法開始,當設計者發現需要更大的靈活性時,設計便會向其他創建型模式演化
  • 工廠方法的實現並不能減少工作量,但是它能夠在必須處理新情況時,避免使已經很複雜的代碼更加複雜

抽象工廠模式

定義:抽象工廠模式(Abstract Factory),提供了一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類

<img da_015>

用抽象工廠模式實現不同數據庫切換

interface IUser{
    void Insert(User user);
    User GetUser(int id);
}

class SqlserverUser : IUser{
    public void Insert(User user){
        Console.WriteLine("在SQL Server中給User表增加一條記錄");
    }

    public User GetUser(int id){
        Console.WriteLine("在SQL Server中根據ID得到User表一條記錄");
        return null;
    }
}

class AccessUser : IUser{
    public void Insert(User user){
        Console.WriteLine("在Access中給User表增加一條記錄");
    }

    public User GetUser(int id){
        Console.WriteLine("在Access中根據ID得到User表一條記錄");
        return null;
    }
}

interface IDepartment{
    void Insert(Department department);
    Department GetDepartment(int id);
}

//用於訪問SQL Server的Department
class SqlserverDepartment : IDepartment{
    public void Insert(Department department){
        Console.WriteLine("在SQL Server中給Department表增加一條記錄");
    }

    public Department GetDepartment(int id){
        Console.WriteLine("在SQL Server中根據ID得到Department表一條記錄");
        return null;
    }
}

//用於訪問Access的Department
class AccessDepartment : IDepartment{
    public void Insert(Department department){
        Console.WriteLine("在Access中給Department表增加一條記錄");
    }

    public Department GetDepartment(int id){
        Console.WriteLine("在Access中根據ID得到Department表一條記錄");
        return null;
    }
}

class DataAccess{
    private static readonly string db = "Sqlserver";
    //private static readonly string db = "Access";

    public static IUser CreateUser(){
        IUser result = null;
        switch(db){
            case "Sqlserver":
                result = new SqlserverUser();
                break;
            case "Access":
                result = new AccessUser();
                break;
        }
        return result;
    }

    public static IDepartment CreateDepartment(){
        IDepartment result = null;
        switch(db){
            case "Sqlserver":
                result = new SqlserverDepartment();
                break;
            case "Access":
                result = new AccessDepartment();
                break;
        }
        return result;
    }
}

客戶端代碼

static void Main(string[] args){
    User user = new User();
    Department dept = new Department();

    //直接得到實際的數據庫訪問實例,而不存在任何依賴
    IUser iu = DataAccess.CreateUser();

    iu.Insert(user);
    iu.GetUser(1);

    //直接得到實際的數據庫訪問實例,而不存在任何依賴
    IDepartment id = DataAccess.CreateDepartment();

    id.Insert(dept);
    id.GetDepartment(1);

    Console.Read();
}

優點:

  • 提供一個創建一系列或相關依賴對象的接口,而無需指定它們具體的類
  • 具體工廠類在一個應用中只需要在初始化的時候出現一次,這就使得改變一個應用的具體工廠變得非常容易
  • 讓具體的創建實例過程與客戶端分離,客戶端是通過它們的抽象接口操縱實例,產品的具體類名也被具體工廠的實現分離,不會出現在客戶端代碼中
  • 可以解決多個類型產品的創建問題

建造者模式

定義: 將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示

<img da_013>

Product類

class Product{
    IList<string> parts = new List<string>();

    public void Add(string part){
        //添加產品部件
        parts.Add(part);
    }

    public void Show(){
        Console.WriteLine("\n產品 創建 --- ");
        foreach(string part in parts){
            Console.WriteLine(part);
        }
    }
}

Builder類,抽象建造者類

abstract class Builder{
    public abstract void BuildPartA();
    public abstract void BuildPartB();
    public abstract Product GetResult();
}

ConcreteBuilder1類,具體建造者類

class ConcreteBuilder1 : Builder{
    private Product product = new Product();

    public override void BuildPartA(){
        product.Add("部件A");
    }

    public override void BuildPartB(){
        product.Add("部件B");
    }

    public override Product GetResult(){
        return product;
    }

}

ConcreteBuilder2類,具體建造者類

class ConcreteBuilder2 : Builder{
    private Product product = new Product();

    public override void BuildPartA(){
        product.Add("部件X");
    }

    public override void BuildPartB(){
        product.Add("部件Y");
    }

    public override Product GetResult(){
        return product;
    }

}

Director類,指揮者類

class Director{
    public void Construct(Builder builder){
        //用來指揮建造過程
        builder.BuilderPartA();
        builder.BuilderPartB();
    }
}

客戶端代碼

static void Main(string[] args){
    Director director = new Director();
    Builder b1 = new ConcreteBuilder1();
    Builder b2 = new ConcreteBuilder2();

    director.Construct(b1);
    //指揮者用ConcreteBuilder1的方法來建造產品
    Product p1 = b1.GetResult();
    p1.Show();

    ...
}

優點:

  • 用戶只需要指定需要建造的類型,無須關心建造的具體過程和細節
  • 建造者隱藏了該產品是如何組裝的,所以若需要改變一個產品的內部表示,只需要再定義一個具體的建造者
  • 用於創建一些複雜的對象,這些對象內部構建間的建造順序通常是穩定的,但對象內部的構建通常面臨着複雜的變化
  • 建造者模式是在當創建複雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時適用的模式

原型模式

定義: 用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象

<img da_010>

原型類

abstract class Prototype{
    private string id;
    public Prototype(string id){
        this.id = id;
    }

    public string Id{
        get{return id;}
    }

    public abstract Prototype Clone();
}

具體原型類

class ConcretePrototype1 : Prototype{
    public ConcreteProtype1(string id) : base(id){

    }

    public override Prototype Clone(){
        //創建當前對象的淺表副本,方法是創建一個新對象,然後將當前對象的非靜態字段複製到該新對象。如果字段是值類型的,則對該字段執行逐位複製。如果字段是引用類型,則複製引用但不復制引用的對象;因此,原始對象及其副本引用同一對象
        return (Prototype)this.MemberwiseClone();
    }
}

客戶端代碼

static void Main(string[] args){
    ConcretePrototype1 p1 = new ConcretePrototype1("I");
    //通過克隆得到新的實例c1
    ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
    Console.WriteLine("Cloned: {0}", c1.Id);
    Console.Read();
}

應用場景:

  • 一般在初始化的信息不發生變化的情況,克隆是最好的辦法,這既隱藏了對象創建的細節,又對性能是大大的提高(不用重新初始化對象,而是動態地獲得對象運行時的狀態)
  • 原型模式其實就是從一個對象再創建另外一個可定製的對象,而且不需知道任何創建的細節
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章