使用C#實現23種常見的設計模式

使用C#實現23種常見的設計模式

這些模式是用於解決常見的物件導向設計問題的最佳實踐。

最後更新 2023/6/8 下午9:51
Token
預計閱讀 46 分鐘
分類
.NET
標籤
.NET C#

設計模式通常分為三個主要類別:

  • 建立型模式

  • 結構型模式

  • 行為型模式。

這些模式是用來解決常見的物件導向設計問題的最佳實踐。

以下是 23 種常見的設計模式並且提供C# 程式碼案例

建立型模式:

1. 單例模式(Singleton)

public sealed class Singleton
{
    //建立一個唯讀的靜態 Singleton 實例
    private static readonly Singleton instance = new Singleton();

    // 記錄 Singleton 的建立次數
    private static int instanceCounter = 0;

    // 單例實例的公共存取點
    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }

    // 私用建構函式
    private Singleton()
    {
        instanceCounter++;
        Console.WriteLine("Instances Created " + instanceCounter);
    }

    // 在此處加入其他的 Singleton 類別方法
    public void LogMessage(string message)
    {
        Console.WriteLine("Message: " + message);
    }
}

在這個例子中,我們有一個名為Singleton的類別,它有一個私用的建構函式和一個靜態的唯讀屬性Instance,用來存取Singleton類別的唯一實例。我們還有一個LogMessage方法,用來模擬Singleton類別的某個行為。

以下是一個使用這個Singleton類別的主控台應用程式:

class Program
{
    static void Main(string[] args)
    {
        Singleton fromEmployee = Singleton.Instance;
        fromEmployee.LogMessage("Message from Employee");

        Singleton fromBoss = Singleton.Instance;
        fromBoss.LogMessage("Message from Boss");
        Console.ReadLine();
    }
}

2. 工廠方法模式(Factory Method)

工廠方法模式是一種建立型設計模式,它提供了一種建立物件的介面,但允許子類別決定要實例化哪個類別。工廠方法讓類別的實例化推遲到子類別中進行。

下面是一個使用 C# 實作的工廠方法模式的簡單範例:

// 抽象產品
public interface IProduct
{
    string Operation();
}

// 具體產品 A
public class ProductA : IProduct
{
    public string Operation()
    {
        return "{Result of ProductA}";
    }
}

// 具體產品 B
public class ProductB : IProduct
{
    public string Operation()
    {
        return "{Result of ProductB}";
    }
}

// 抽象建立者
public abstract class Creator
{
    public abstract IProduct FactoryMethod();
}

// 具體建立者 A
public class CreatorA : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ProductA();
    }
}

// 具體建立者 B
public class CreatorB : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ProductB();
    }
}

以上程式碼中定義了兩個產品ProductAProductB,這兩個產品都實作了IProduct介面。接著我們有兩個 Creator 類別,CreatorACreatorB,它們都繼承自抽象基底類別CreatorCreatorA工廠建立ProductACreatorB工廠建立ProductB

以下是一個使用這些工廠和產品的範例:

class Program
{
    static void Main(string[] args)
    {
        // 建立工廠物件
        Creator creatorA = new CreatorA();
        Creator creatorB = new CreatorB();

        // 透過工廠方法建立產品物件
        IProduct productA = creatorA.FactoryMethod();
        IProduct productB = creatorB.FactoryMethod();

        // 列印結果
        Console.WriteLine("ProductA says: " + productA.Operation());
        Console.WriteLine("ProductB says: " + productB.Operation());

        Console.ReadLine();
    }
}

當你執行這個程式時,它會顯示出ProductAProductBOperation方法回傳的結果。這說明我們已經成功地使用工廠方法模式建立了產品實例。每個工廠類別決定了它建立哪個產品的實例。這種方式使得用戶端程式碼不需要直接實例化產品類別,而只需要依賴工廠介面,增加了程式的靈活性。

3. 抽象工廠模式(Abstract Factory)

抽象工廠模式是一種建立型設計模式,它提供了一種介面,用於建立相關或依賴物件的系列,而不指定這些物件的具體類別。在這個模式中,用戶端透過他們的抽象介面使用類別,允許該模式在不影響用戶端的情況下取代實作類別。

以下是一個簡單的抽象工廠模式的 C# 實作:

// 抽象產品:動物
public interface IAnimal
{
    string Speak();
}

// 具體產品:狗
public class Dog : IAnimal
{
    public string Speak()
    {
        return "Bark Bark";
    }
}

// 具體產品:貓
public class Cat : IAnimal
{
    public string Speak()
    {
        return "Meow Meow";
    }
}

// 抽象工廠
public abstract class IAnimalFactory
{
    public abstract IAnimal CreateAnimal();
}

// 具體工廠:狗工廠
public class DogFactory : IAnimalFactory
{
    public override IAnimal CreateAnimal()
    {
        return new Dog();
    }
}

// 具體工廠:貓工廠
public class CatFactory : IAnimalFactory
{
    public override IAnimal CreateAnimal()
    {
        return new Cat();
    }
}

以上程式碼定義了兩種動物DogCat,它們都實作了IAnimal介面。然後我們有兩個工廠類別,DogFactoryCatFactory,它們都繼承自IAnimalFactoryDogFactory生產Dog,而CatFactory生產Cat

以下是一個使用這些工廠和產品的範例:

class Program
{
    static void Main(string[] args)
    {
        // 建立工廠
        IAnimalFactory dogFactory = new DogFactory();
        IAnimalFactory catFactory = new CatFactory();

        // 使用工廠建立產品
        IAnimal dog = dogFactory.CreateAnimal();
        IAnimal cat = catFactory.CreateAnimal();

        // 列印結果
        Console.WriteLine("Dog says: " + dog.Speak());
        Console.WriteLine("Cat says: " + cat.Speak());

        Console.ReadLine();
    }
}

當你執行這個程式時,會列印出 Dog 和 Cat 的 Speak 方法的結果,這顯示了我們已經成功地使用了抽象工廠模式建立了產品實例。這種方式使得用戶端程式碼不需要直接實例化產品類別,而只需要依賴工廠介面,增加了程式的靈活性和擴充性。

4. 建造者模式(Builder)

建造者模式是一種建立型設計模式,它提供了一種建立物件的介面,但是允許使用相同的建構過程來建立不同的產品。

以下是在 C# 中實作建造者模式的一個簡單範例:

// 產品
public class Car
{
    public string Engine { get; set; }
    public string Wheels { get; set; }
    public string Doors { get; set; }
}

// 建造者抽象類別
public abstract class CarBuilder
{
    protected Car car;

    public void CreateNewCar()
    {
        car = new Car();
    }

    public Car GetCar()
    {
        return car;
    }

    public abstract void SetEngine();
    public abstract void SetWheels();
    public abstract void SetDoors();
}

// 具體建造者
public class FerrariBuilder : CarBuilder
{
    public override void SetEngine()
    {
        car.Engine = "V8";
    }

    public override void SetWheels()
    {
        car.Wheels = "18 inch";
    }

    public override void SetDoors()
    {
        car.Doors = "2";
    }
}

// 指揮者
public class Director
{
    public Car Construct(CarBuilder carBuilder)
    {
        carBuilder.CreateNewCar();
        carBuilder.SetEngine();
        carBuilder.SetWheels();
        carBuilder.SetDoors();
        return carBuilder.GetCar();
    }
}

以上程式碼中,Car是我們要建立的產品,CarBuilder是抽象的建造者,定義了製造一個產品所需要的各個步驟,FerrariBuilder是具體的建造者,實作了CarBuilder定義的所有步驟,Director是指揮者,它告訴建造者應該按照什麼順序去執行哪些步驟。

以下是一個使用這個建造者模式的範例:

class Program
{
    static void Main(string[] args)
    {
        Director director = new Director();
        CarBuilder builder = new FerrariBuilder();
        Car ferrari = director.Construct(builder);

        Console.WriteLine($"Engine: {ferrari.Engine}, Wheels: {ferrari.Wheels}, Doors: {ferrari.Doors}");
        Console.ReadLine();
    }
}

當你執行這個程式時,會看到我們已經成功地建立了一個Car實例,它的各個部分是按照FerrariBuilder所定義的方式建立的。這說明我們使用建造者模式成功地將一個複雜物件的建構過程解耦,使得同樣的建構過程可以建立不同的表示。

5. 原型模式(Prototype)

原型模式是一種建立型設計模式,它實作了一個原型介面,該介面用於建立當前物件的複製。當直接建立物件的代價比較大時,則採用這種模式。例如,一個物件需要在一個高代價的資料庫操作後被建立。

以下是在 C# 中實作原型模式的一個簡單範例:

// 抽象原型
public interface IPrototype
{
    IPrototype Clone();
}

// 具體原型
public class ConcretePrototype : IPrototype
{
    public string Name { get; set; }
    public int Value { get; set; }

    public IPrototype Clone()
    {
        // 實作深複製
        return (ConcretePrototype)this.MemberwiseClone(); // Clones the concrete object.
    }
}

以上程式碼定義了一個ConcretePrototype類別,它實作了IPrototype介面。介面定義了一個Clone方法,用於複製物件。在ConcretePrototype類別中,我們使用了MemberwiseClone方法來建立一個新的複製物件。

以下是一個使用原型模式的範例:

class Program
{
    static void Main(string[] args)
    {
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.Name = "Original";
        prototype.Value = 10;

        Console.WriteLine("Original instance: " + prototype.Name + ", " + prototype.Value);

        ConcretePrototype clone = (ConcretePrototype)prototype.Clone();
        Console.WriteLine("Cloned instance: " + clone.Name + ", " + clone.Value);

        Console.ReadLine();
    }
}

在這個例子中,我們建立了一個ConcretePrototype物件,並為其屬性賦值,然後我們呼叫Clone方法建立了一個新的ConcretePrototype物件。當我們執行這個程式時,會看到原始物件和複製物件的屬性是相同的,這表明我們已經成功地複製了一個物件。

執行流程如下:

  1. 建立一個具體的原型物件,為其屬性賦值。
  2. 呼叫原型物件的Clone方法,建立一個新的物件,該物件的屬性與原型物件的屬性相同。
  3. 列印原型物件和複製物件的屬性,驗證它們是否相同。

結構型模式:

1. 轉接器模式(Adapter)

轉接器模式(Adapter Pattern)的目標是將一個類別的介面轉換成用戶端期望的另一個介面。轉接器使得原本由於介面不相容而不能在一起工作的那些類別可以在一起工作。下面是一個使用 C# 實作的轉接器模式的例子:

在這個例子中,我將建立一個ITarget介面和一個Adaptee類別。然後我將建立一個轉接器Adapter,它會實作ITarget介面並使用Adaptee類別的方法來滿足ITarget的要求。

// 目標介面,或者用戶端期望的介面。
public interface ITarget
{
    string GetRequest();
}

// 需要轉接的類別。
public class Adaptee
{
    public string GetSpecificRequest()
    {
        return "Specific request.";
    }
}

// 轉接器類別,它透過在內部封裝一個 Adaptee 物件來滿足 ITarget 的介面。
public class Adapter : ITarget
{
    private readonly Adaptee _adaptee;

    public Adapter(Adaptee adaptee)
    {
        this._adaptee = adaptee;
    }

    public string GetRequest()
    {
        return $"This is '{this._adaptee.GetSpecificRequest()}'";
    }
}

// 用戶端程式碼,它與所有符合 ITarget 介面的物件相容。
public class Client
{
    public void MakeRequest(ITarget target)
    {
        Console.WriteLine(target.GetRequest());
    }
}

執行流程如下:

class Program
{
    static void Main(string[] args)
    {
        Adaptee adaptee = new Adaptee();
        ITarget target = new Adapter(adaptee);
        Client client = new Client();

        // 由於轉接器可以使得 Adaptee 與 Client 相容,所以我們可以呼叫 Client 的 MakeRequest 方法
        client.MakeRequest(target);

        // 等待使用者輸入,以防止主控台程式立即結束。
        Console.ReadKey();
    }
}

當執行上述程式碼時,將在主控台上列印出以下輸出:

This is 'Specific request.'

此例中,Adapter是將Adaptee轉接到ITarget介面的類別。當用戶端(在這種情況下是Client類別)呼叫AdapterGetRequest方法時,Adapter會將這個請求轉送給AdapteeGetSpecificRequest方法。

在此實例中,我們透過使用Adapter類別,讓原本介面不相容的Client類別和Adaptee類別能夠順利地一起工作。

2. 橋接模式(Bridge)

橋接模式是一種結構型設計模式,用於將抽象部分與其實作部分分離,使它們都可以獨立地變化。

以下是在 C# 中實作橋接模式的一個簡單範例:

// 實作類別介面
public interface IImplementor
{
    void OperationImp();
}

// 具體實作類別 A
public class ConcreteImplementorA : IImplementor
{
    public void OperationImp()
    {
        Console.WriteLine("Concrete Implementor A");
    }
}

// 具體實作類別 B
public class ConcreteImplementorB : IImplementor
{
    public void OperationImp()
    {
        Console.WriteLine("Concrete Implementor B");
    }
}

// 抽象類別
public abstract class Abstraction
{
    protected IImplementor implementor;

    public Abstraction(IImplementor implementor)
    {
        this.implementor = implementor;
    }

    public virtual void Operation()
    {
        implementor.OperationImp();
    }
}

// 擴充的抽象類別
public class RefinedAbstraction : Abstraction
{
    public RefinedAbstraction(IImplementor implementor) : base(implementor) { }

    public override void Operation()
    {
        Console.WriteLine("Refined Abstraction is calling implementor's method:");
        base.Operation();
    }
}

在這個程式碼中,Abstraction是抽象類別,它有一個IImplementor介面的實例,透過這個實例呼叫實作類別的方法。RefinedAbstraction是擴充的抽象類別,它繼承自AbstractionConcreteImplementorAConcreteImplementorB是實作類別,它們實作了IImplementor介面。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        IImplementor implementorA = new ConcreteImplementorA();
        Abstraction abstractionA = new RefinedAbstraction(implementorA);
        abstractionA.Operation();

        IImplementor implementorB = new ConcreteImplementorB();
        Abstraction abstractionB = new RefinedAbstraction(implementorB);
        abstractionB.Operation();

        Console.ReadLine();
    }
}

在這個例子中,我們建立了兩個實作類別的實例,然後建立了兩個抽象類別的實例,每個抽象類別的實例都有一個實作類別的實例。當我們呼叫抽象類別的Operation方法時,它會呼叫實作類別的OperationImp方法。

執行流程如下:

  1. 建立實作類別的實例。
  2. 建立抽象類別的實例,抽象類別的實例有一個實作類別的實例。
  3. 呼叫抽象類別的Operation方法,該方法會呼叫實作類別的OperationImp方法。

3. 組合模式(Composite)

組合模式(Composite pattern)是一種結構型設計模式,它可以使你將物件組合成樹狀結構,並且能像使用獨立物件一樣使用它們。這種模式的主要目的是使單一物件和組合物件具有一致性。

以下是在 C# 中實作組合模式的一個簡單範例:

// 抽象元件類別
public abstract class Component
{
    protected string name;

    public Component(string name)
    {
        this.name = name;
    }

    public abstract void Add(Component c);
    public abstract void Remove(Component c);
    public abstract void Display(int depth);
}

// 葉節點類別
public class Leaf : Component
{
    public Leaf(string name) : base(name) { }

    public override void Add(Component c)
    {
        Console.WriteLine("Cannot add to a leaf");
    }

    public override void Remove(Component c)
    {
        Console.WriteLine("Cannot remove from a leaf");
    }

    public override void Display(int depth)
    {
        Console.WriteLine(new String('-', depth) + name);
    }
}

// 構件容器類別
public class Composite : Component
{
    private List<Component> _children = new List<Component>();

    public Composite(string name) : base(name) { }

    public override void Add(Component component)
    {
        _children.Add(component);
    }

    public override void Remove(Component component)
    {
        _children.Remove(component);
    }

    public override void Display(int depth)
    {
        Console.WriteLine(new String('-', depth) + name);

        // 顯示每個節點的子節點
        foreach (Component component in _children)
        {
            component.Display(depth + 2);
        }
    }
}

在這個程式碼中,Component是元件抽象類別,它有一個名字,並定義了新增、刪除和顯示操作。Leaf是葉子節點,它實作了Component的操作。Composite是元件容器,它可以新增、刪除和顯示其子節點。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        Composite root = new Composite("root");
        root.Add(new Leaf("Leaf A"));
        root.Add(new Leaf("Leaf B"));

        Composite comp = new Composite("Composite X");
        comp.Add(new Leaf("Leaf XA"));
        comp.Add(new Leaf("Leaf XB"));

        root.Add(comp);

        Composite comp2 = new Composite("Composite XY");
        comp2.Add(new Leaf("Leaf XYA"));
        comp2.Add(new Leaf("Leaf XYB"));

        comp.Add(comp2);

        root.Add(new Leaf("Leaf C"));

        // 在組合中新增和刪除
        Leaf leaf = new Leaf("Leaf D");
        root.Add(leaf);
        root.Remove(leaf);

        // 顯示樹狀結構
        root.Display(1);

        Console.ReadLine();
    }
}

在這個例子中,我們建立了一個根節點,並在其中新增了兩個葉子節點。然後我們建立了一個複合節點,並在其中新增了兩個葉子節點,然後我們把複合節點新增到根節點中。我們還在複合節點中新增了另一個複合節點。最後,我們又在根節點中新增和刪除了一個葉子節點,然後顯示了樹的結構。

執行流程如下:

  1. 建立組合和葉子物件。
  2. 透過呼叫組合物件的Add方法將葉子物件和其他組合物件新增到組合物件中。
  3. 透過呼叫組合物件的Remove方法將葉子物件從組合物件中移除。
  4. 呼叫組合物件的Display方法顯示組合物件的結構。

4. 裝飾模式(Decorator)

裝飾模式是一種結構型設計模式,它允許在執行時期動態地將功能新增到物件中,這種模式提供了比繼承更有彈性的解決方案。

以下是在 C# 中實作裝飾模式的一個簡單範例:

// 抽象元件
public abstract class Component
{
    public abstract string Operation();
}

// 具體元件
public class ConcreteComponent : Component
{
    public override string Operation()
    {
        return "ConcreteComponent";
    }
}

// 抽象裝飾器
public abstract class Decorator : Component
{
    protected Component component;

    public Decorator(Component component)
    {
        this.component = component;
    }

    public override string Operation()
    {
        if (component != null)
        {
            return component.Operation();
        }
        else
        {
            return string.Empty;
        }
    }
}

// 具體裝飾器 A
public class ConcreteDecoratorA : Decorator
{
    public ConcreteDecoratorA(Component comp) : base(comp) { }

    public override string Operation()
    {
        return $"ConcreteDecoratorA({base.Operation()})";
    }
}

// 具體裝飾器 B
public class ConcreteDecoratorB : Decorator
{
    public ConcreteDecoratorB(Component comp) : base(comp) { }

    public override string Operation()
    {
        return $"ConcreteDecoratorB({base.Operation()})";
    }
}

在這個程式碼中,Component是一個抽象元件,它定義了一個Operation方法。ConcreteComponent是具體元件,它實作了ComponentOperation方法。Decorator是一個抽象裝飾器,它包含一個Component物件,並覆寫了Operation方法。ConcreteDecoratorAConcreteDecoratorB是具體的裝飾器,它們繼承了Decorator並覆寫了Operation方法,以新增新的功能。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        // 基本元件
        Component component = new ConcreteComponent();
        Console.WriteLine("Basic Component: " + component.Operation());

        // 裝飾後的元件
        Component decoratorA = new ConcreteDecoratorA(component);
        Console.WriteLine("A Decorated: " + decoratorA.Operation());

        Component decoratorB = new ConcreteDecoratorB(decoratorA);
        Console.WriteLine("B Decorated: " + decoratorB.Operation());

        Console.ReadLine();
    }
}

在這個例子中,我們首先建立了一個ConcreteComponent物件,並呼叫它的Operation方法。然後我們建立了一個ConcreteDecoratorA物件,它裝飾了ConcreteComponent,並呼叫它的Operation方法。最後,我們建立了一個ConcreteDecoratorB物件,它裝飾了ConcreteDecoratorA,並呼叫它的Operation方法。這樣,我們就可以在執行時期動態地新增功能。

執行流程如下:

  1. 建立一個具體元件物件並呼叫其操作。
  2. 建立一個裝飾器物體,該物體裝飾了具體元件,並呼叫其操作。在操作中,裝飾器首先呼叫具體元件的操作,然後執行額外的操作。
  3. 建立另一個裝飾器物體,裝飾前一個裝飾器,並呼叫其操作。在操作中,這個裝飾器首先呼叫前一個裝飾器的操作,然後執行額外的操作。

5. 外觀模式(Facade)

外觀模式是一種結構型設計模式,提供了一個統一的介面,用來存取子系統中的一群介面。外觀模式定義了一個高層介面,讓子系統更容易使用。

以下是在 C# 中實作外觀模式的一個簡單範例:

// 子系統 A
public class SubSystemA
{
    public string OperationA()
    {
        return "SubSystemA, OperationA\n";
    }
}

// 子系統 B
public class SubSystemB
{
    public string OperationB()
    {
        return "SubSystemB, OperationB\n";
    }
}

// 子系統 C
public class SubSystemC
{
    public string OperationC()
    {
        return "SubSystemC, OperationC\n";
    }
}

// 外觀類別
public class Facade
{
    private SubSystemA a = new SubSystemA();
    private SubSystemB b = new SubSystemB();
    private SubSystemC c = new SubSystemC();

    public string OperationWrapper()
    {
        string result = "Facade initializes subsystems:\n";
        result += a.OperationA();
        result += b.OperationB();
        result += c.OperationC();
        return result;
    }
}

在這個程式碼中,SubSystemASubSystemBSubSystemC都是子系統,每個子系統都有一個操作。Facade是一個外觀類別,它封裝了對子系統的操作,提供了一個統一的介面。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        Facade facade = new Facade();
        Console.WriteLine(facade.OperationWrapper());

        Console.ReadLine();
    }
}

在這個例子中,我們建立了一個Facade物件,並呼叫了它的OperationWrapper方法。這個方法封裝了對子系統的操作,使得用戶端可以不直接操作子系統,而是透過外觀類別操作子系統。

執行流程如下:

  1. 建立一個外觀物件。

  2. 透過呼叫外觀物件的方法,間接地操作子系統。

  3. 子系統的操作被封裝在外觀物件的方法中,用戶端不需要直接操作子系統。

6. 享元模式(Flyweight)

享元模式(Flyweight Pattern)是一種結構型設計模式,該模式主要用於減少建立物件的數量,以減少記憶體佔用和提高效能。這種類型的設計模式屬於結構型模式,它提供了一種減少物件數量從而改善應用所需的物件結構的方式。

以下是在 C# 中實作享元模式的一個簡單範例:

// 享元類別
public class Flyweight
{
    private string intrinsicState;

    // 建構函式
    public Flyweight(string intrinsicState)
    {
        this.intrinsicState = intrinsicState;
    }

    // 業務方法
    public void Operation(string extrinsicState)
    {
        Console.WriteLine($"Intrinsic State = {intrinsicState}, Extrinsic State = {extrinsicState}");
    }
}

// 享元工廠類別
public class FlyweightFactory
{
    private Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>();

    public Flyweight GetFlyweight(string key)
    {
        if (!flyweights.ContainsKey(key))
        {
            flyweights[key] = new Flyweight(key);
        }

        return flyweights[key];
    }

    public int GetFlyweightCount()
    {
        return flyweights.Count;
    }
}

在這個程式碼中,Flyweight是享元類別,它有一個內在狀態intrinsicState,這個狀態是不變的。FlyweightFactory是享元工廠類別,它維護了一個享元物件的集合。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        FlyweightFactory factory = new FlyweightFactory();

        Flyweight flyweightA = factory.GetFlyweight("A");
        flyweightA.Operation("A operation");

        Flyweight flyweightB = factory.GetFlyweight("B");
        flyweightB.Operation("B operation");

        Flyweight flyweightC = factory.GetFlyweight("A");
        flyweightC.Operation("C operation");

        Console.WriteLine($"Total Flyweights: {factory.GetFlyweightCount()}");

        Console.ReadLine();
    }
}

在這個例子中,我們建立了一個FlyweightFactory物件,並透過它建立了兩個享元物件。注意,當我們試圖建立第三個享元物件時,工廠實際上回傳了第一個享元物件的參考,因為這兩個物件的內在狀態是相同的。

執行流程如下:

  1. 建立一個享元工廠物件。
  2. 透過享元工廠取得享元物件。如果物件已經存在,則回傳現有物件;否則,建立新物件。
  3. 執行享元物件的操作。
  4. 顯示目前享元物件的數量。

7. 代理模式(Proxy)

代理模式是一種結構型設計模式,它提供了一個物件代替另一個物件來控制對它的存取。代理物件可以在用戶端和目標物件之間起到中介的作用,並加入其他的功能。

以下是在 C# 中實作代理模式的一個簡單範例:

// 抽象主題介面
public interface ISubject
{
    void Request();
}

// 真實主題
public class RealSubject : ISubject
{
    public void Request()
    {
        Console.WriteLine("RealSubject: Handling Request.");
    }
}

// 代理
public class Proxy : ISubject
{
    private RealSubject _realSubject;

    public Proxy(RealSubject realSubject)
    {
        this._realSubject = realSubject;
    }

    public void Request()
    {
        if (this.CheckAccess())
        {
            this._realSubject.Request();
            this.LogAccess();
        }
    }

    public bool CheckAccess()
    {
        // 檢查是否有權限存取
        Console.WriteLine("Proxy: Checking access prior to firing a real request.");
        return true;
    }

    public void LogAccess()
    {
        // 記錄請求
        Console.WriteLine("Proxy: Logging the time of request.");
    }
}

在這個程式碼中,ISubject是一個介面,定義了Request方法。RealSubject是實作了ISubject介面的類別,Proxy是代理類別,它也實作了ISubject介面,並持有一個RealSubject物件的參考。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Client: Executing the client code with a real subject:");
        RealSubject realSubject = new RealSubject();
        realSubject.Request();

        Console.WriteLine();

        Console.WriteLine("Client: Executing the same client code with a proxy:");
        Proxy proxy = new Proxy(realSubject);
        proxy.Request();

        Console.ReadLine();
    }
}

在這個例子中,我們首先直接呼叫了RealSubjectRequest方法,然後我們透過代理呼叫了相同的方法。注意,在透過代理呼叫Request方法時,代理還執行了其他的操作,如檢查存取權限和記錄日誌。

執行流程如下:

  1. 建立一個真實主題物件,並直接呼叫其Request方法。
  2. 建立一個代理物件,代理物件包含一個真實主題的參考。
  3. 透過代理物件呼叫Request方法。在這個方法中,代理首先檢查存取權限,然後呼叫真實主題的Request方法,最後記錄日誌。

行為型模式:

1. 責任鏈模式(Chain of Responsibility)

責任鏈模式(Chain of Responsibility Pattern)是一種行為設計模式,該模式為請求建立了一個接收物件的鏈。這種模式給予請求的物件更多的自由度,並允許請求沿著鏈路傳遞,直到有一個物件處理它為止。

在這個例子中,我將建立一個Handler抽象類別和兩個具體的處理器ConcreteHandler1ConcreteHandler2。每個處理器都檢查它是否可以處理請求,如果可以,它就處理該請求。如果不行,它就將請求傳遞給鏈中的下一個處理器。

public abstract class Handler
{
    protected Handler successor;

    public void SetSuccessor(Handler successor)
    {
        this.successor = successor;
    }

    public abstract void HandleRequest(int request);
}

public class ConcreteHandler1 : Handler
{
    public override void HandleRequest(int request)
    {
        if (request >= 0 && request < 10)
        {
            Console.WriteLine($"{this.GetType().Name} handled request {request}");
        }
        else if (successor != null)
        {
            successor.HandleRequest(request);
        }
    }
}

public class ConcreteHandler2 : Handler
{
    public override void HandleRequest(int request)
    {
        if (request >= 10 && request < 20)
        {
            Console.WriteLine($"{this.GetType().Name} handled request {request}");
        }
        else if (successor != null)
        {
            successor.HandleRequest(request);
        }
    }
}

執行流程如下:

class Program
{
    static void Main(string[] args)
    {
        // 設定責任鏈
        Handler h1 = new ConcreteHandler1();
        Handler h2 = new ConcreteHandler2();

        h1.SetSuccessor(h2);

        // 產生並處理請求
        int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 };

        foreach (int request in requests)
        {
            h1.HandleRequest(request);
        }

        Console.ReadKey();
    }
}

當執行上述程式碼時,將在主控台上列印出以下輸出:

ConcreteHandler1 handled request 2
ConcreteHandler1 handled request 5
ConcreteHandler2 handled request 14
ConcreteHandler2 handled request 18
ConcreteHandler1 handled request 3

在這個例子中,ConcreteHandler1ConcreteHandler2都是處理器,它們決定是否處理傳入的請求。如果ConcreteHandler1無法處理請求,它將請求傳遞給ConcreteHandler2。如果ConcreteHandler2也無法處理請求,那麼請求就會被忽略。

這個例子顯示了責任鏈模式的核心思想,即建立一個物件鏈來處理一個請求。在這個鏈中,每個處理器決定是否處理該請求。如果它不能處理該請求,它就將請求傳遞給鏈中的下一個處理器。

2. 命令模式(Command)

命令模式(Command Pattern)是一種資料驅動的設計模式,它屬於行為型模式。在命令模式中,請求在物件中封裝成為一個操作或行為,這些請求被送到呼叫物件,呼叫物件尋找可以處理該命令的合適的物件,並把命令直接送達到對應的物件,該物件會執行這些命令。

以下是在 C# 中實作命令模式的一個簡單範例:

// 命令介面
public interface ICommand
{
    void Execute();
}

// 具體命令類別
public class ConcreteCommand : ICommand
{
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver)
    {
        this.receiver = receiver;
    }

    public void Execute()
    {
        receiver.Action();
    }
}

// 接收者類別
public class Receiver
{
    public void Action()
    {
        Console.WriteLine("Receiver performs an action");
    }
}

// 呼叫者或發送者類別
public class Invoker
{
    private ICommand command;

    public void SetCommand(ICommand command)
    {
        this.command = command;
    }

    public void ExecuteCommand()
    {
        command.Execute();
    }
}

在這個程式碼中,ICommand是命令介面,定義了Execute方法。ConcreteCommand是具體的命令類別,它實作了ICommand介面,並持有一個Receiver物件的參考。Invoker是呼叫者或發送者類別,它持有一個ICommand物件的參考,並可以透過SetCommand方法設定命令,透過ExecuteCommand方法執行命令。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        Receiver receiver = new Receiver();
        ICommand command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker();

        invoker.SetCommand(command);
        invoker.ExecuteCommand();

        Console.ReadLine();
    }
}

在這個例子中,我們建立了一個Receiver物件、一個ConcreteCommand物件和一個Invoker物件。然後我們透過InvokerSetCommand方法設定了命令,並透過ExecuteCommand方法執行了命令。

執行流程如下:

  1. 建立一個接收者物件。
  2. 建立一個具體命令物件,並將接收者物件傳遞給它。
  3. 建立一個呼叫者或發送者物件。
  4. 透過呼叫者物件的SetCommand方法設定命令。
  5. 透過呼叫者物件的ExecuteCommand方法執行命令。

3. 直譯器模式(Interpreter)

直譯器模式(Interpreter Pattern)是一種行為型設計模式,用於解決一些固定語法格式的需求。它定義了如何在語言中表示和解析語法。

以下是在 C# 中實作直譯器模式的一個簡單範例:

// 抽象表達式
public interface IExpression
{
    bool Interpret(string context);
}

// 終結符表達式
public class TerminalExpression : IExpression
{
    private string data;

    public TerminalExpression(string data)
    {
        this.data = data;
    }

    public bool Interpret(string context)
    {
        if (context.Contains(data))
        {
            return true;
        }

        return false;
    }
}

// 非終結符表達式
public class OrExpression : IExpression
{
    private IExpression expr1 = null;
    private IExpression expr2 = null;

    public OrExpression(IExpression expr1, IExpression expr2)
    {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    public bool Interpret(string context)
    {
        return expr1.Interpret(context) || expr2.Interpret(context);
    }
}

在這個程式碼中,IExpression是抽象表達式,定義了Interpret方法。TerminalExpression是終結符表達式,它實作了IExpression介面。OrExpression是非終結符表達式,它也實作了IExpression介面。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        IExpression isMale = GetMaleExpression();
        IExpression isMarriedWoman = GetMarriedWomanExpression();

        Console.WriteLine($"John is male? {isMale.Interpret("John")}");
        Console.WriteLine($"Julie is a married women? {isMarriedWoman.Interpret("Married Julie")}");

        Console.ReadLine();
    }

    // 規則:Robert 和 John 是男性
    public static IExpression GetMaleExpression()
    {
        IExpression robert = new TerminalExpression("Robert");
        IExpression john = new TerminalExpression("John");
        return new OrExpression(robert, john);
    }

    // 規則:Julie 是一個已婚的女性
    public static IExpression GetMarriedWomanExpression()
    {
        IExpression julie = new TerminalExpression("Julie");
        IExpression married = new TerminalExpression("Married");
        return new OrExpression(julie, married);
    }
}

在這個例子中,我們定義了兩個規則,"Robert 和 John 是男性"和"Julie 是一個已婚的女性"。我們然後建立了兩個表達式物件,分別表示這兩個規則,並使用這兩個物件來解析輸入。

執行流程如下:

  1. 建立終結符表達式物件和非終結符表達式物件,用於表示規則。
  2. 呼叫表達式物件的Interpret方法,解析輸入的字串。
  3. 輸出解析結果。

4. 迭代器模式(Iterator)

迭代器模式(Iterator Pattern)是一種行為型設計模式,它提供了一種方法來存取一個物件的元素,而不需要暴露該物件的內部表示。以下是在 C# 中實作迭代器模式的一個簡單範例:

// 抽象聚合類別
public interface IAggregate
{
    IIterator CreateIterator();
    void Add(string item);
    int Count { get; }
    string this[int index] { get; set; }
}

// 具體聚合類別
public class ConcreteAggregate : IAggregate
{
    private List<string> items = new List<string>();

    public IIterator CreateIterator()
    {
        return new ConcreteIterator(this);
    }

    public int Count
    {
        get { return items.Count; }
    }

    public string this[int index]
    {
        get { return items[index]; }
        set { items.Insert(index, value); }
    }

    public void Add(string item)
    {
        items.Add(item);
    }
}

// 抽象迭代器
public interface IIterator
{
    string First();
    string Next();
    bool IsDone { get; }
    string CurrentItem { get; }
}

// 具體迭代器
public class ConcreteIterator : IIterator
{
    private ConcreteAggregate aggregate;
    private int current = 0;

    public ConcreteIterator(ConcreteAggregate aggregate)
    {
        this.aggregate = aggregate;
    }

    public string First()
    {
        return aggregate[0];
    }

    public string Next()
    {
        string ret = null;
        if (current < aggregate.Count - 1)
        {
            ret = aggregate[++current];
        }

        return ret;
    }

    public string CurrentItem
    {
        get { return aggregate[current]; }
    }

    public bool IsDone
    {
        get { return current >= aggregate.Count; }
    }
}

在這個程式碼中,IAggregate是抽象聚合類別,定義了CreateIterator等方法,ConcreteAggregate是具體聚合類別,實作了IAggregate介面。IIterator是抽象迭代器,定義了FirstNext等方法,ConcreteIterator是具體迭代器,實作了IIterator介面。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        IAggregate aggregate = new ConcreteAggregate();
        aggregate.Add("Item A");
        aggregate.Add("Item B");
        aggregate.Add("Item C");
        aggregate.Add("Item D");

        IIterator iterator = aggregate.CreateIterator();

        Console.WriteLine("Iterating over collection:");

        string item = iterator.First();
        while (item != null)
        {
            Console.WriteLine(item);
            item = iterator.Next();
        }

        Console.ReadLine();
    }
}

在這個例子中,我們建立了一個ConcreteAggregate物件,並加入了幾個元素。然後我們透過CreateIterator方法建立了一個迭代器,並使用這個迭代器遍歷了集合中的所有元素。

執行流程如下:

  1. 建立一個聚合物件,並加入一些元素。
  2. 透過聚合物件的CreateIterator方法建立一個迭代器。
  3. 透過迭代器的First方法取得第一個元素,然後透過Next方法取得後續的元素,直到取得不到元素為止。

5. 中介者模式(Mediator)

中介者模式是一種行為設計模式,它讓你能減少一組物件之間複雜的通訊。它提供了一個中介者物件,此物件負責在組中的物件之間進行通訊,而不是這些物件直接進行通訊。

首先,讓我們定義一個中介者介面和一個具體的中介者:

// Mediator 介面宣告了與元件互動的方法。
public interface IMediator
{
    void Notify(object sender, string ev);
}

// 具體 Mediators 實作協作行為,它負責協調多個元件。
public class ConcreteMediator : IMediator
{
    private Component1 _component1;
    private Component2 _component2;

    public ConcreteMediator(Component1 component1, Component2 component2)
    {
        _component1 = component1;
        _component1.SetMediator(this);
        _component2 = component2;
        _component2.SetMediator(this);
    }

    public void Notify(object sender, string ev)
    {
        if (ev == "A")
        {
            Console.WriteLine("Mediator reacts on A and triggers following operations:");
            this._component2.DoC();
        }
        if (ev == "D")
        {
            Console.WriteLine("Mediator reacts on D and triggers following operations:");
            this._component1.DoB();
            this._component2.DoC();
        }
    }
}

接著,我們定義一個基礎元件類別和兩個具體元件:

public abstract class BaseComponent
{
    protected IMediator _mediator;

    public BaseComponent(IMediator mediator = null)
    {
        _mediator = mediator;
    }

    public void SetMediator(IMediator mediator)
    {
        this._mediator = mediator;
    }
}

// 具體 Components 實作各種功能。它們不依賴於其他元件。
// 它們也不依賴於任何具體 Mediator 類別。
public class Component1 : BaseComponent
{
    public void DoA()
    {
        Console.WriteLine("Component 1 does A.");
        this._mediator.Notify(this, "A");
    }

    public void DoB()
    {
        Console.WriteLine("Component 1 does B.");
        this._mediator.Notify(this, "B");
    }
}

public class Component2 : BaseComponent
{
    public void DoC()
    {
        Console.WriteLine("Component 2 does C.");
        this._mediator.Notify(this, "C");
    }

    public void DoD()
    {
        Console.WriteLine("Component 2 does D.");
        this._mediator.Notify(this, "D");
    }
}

最後,我們來建立一個用戶端程式碼:

class Program
{
    static void Main(string[] args)
    {
        // The client code.
        Component1 component1 = new Component1();
        Component2 component2 = new Component2();
        new ConcreteMediator(component1, component2);

        Console.WriteLine("Client triggers operation A.");
        component1.DoA();

        Console.WriteLine();

        Console.WriteLine("Client triggers operation D.");
        component2.DoD();
    }
}

這個範例中的各個元件透過中介者來進行通訊,而不是直接通訊,這樣就可以減少元件之間的依賴性,使得它們可以更容易地被獨立修改。當一個元件發生某個事件(例如"Component 1 does A")時,它會透過中介者來通知其他元件,這樣其他元件就可以根據這個事件來做出回應(例如"Component 2 does C")。

6. 備忘錄模式(Memento)

備忘錄模式是一種行為設計模式,它能儲存物件的狀態,以便在後面可以恢復它。在大多數情況下,這種模式可以讓你在不破壞物件封裝的前提下,儲存和恢復物件的歷史狀態。

以下是一個簡單的備忘錄模式的實作,其中有三個主要的類別:Originator(儲存了一個重要的狀態,這個狀態可能會隨著時間改變),Memento(儲存了Originator的一個快照,這個快照包含了Originator的狀態),以及Caretaker(負責儲存Memento)。

// Originator 類別可以產生一個備忘錄,並且可以透過備忘錄恢復其狀態。
public class Originator
{
    private string _state;

    public Originator(string state)
    {
        this._state = state;
        Console.WriteLine($"Originator: My initial state is: {_state}");
    }

    public void DoSomething()
    {
        Console.WriteLine("Originator: I'm doing something important.");
        _state = GenerateRandomString(30);
        Console.WriteLine($"Originator: and my state has changed to: {_state}");
    }

    private string GenerateRandomString(int length = 10)
    {
        string allowedSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        string result = string.Empty;

        while (length > 0)
        {
            result += allowedSymbols[new Random().Next(0, allowedSymbols.Length)];

            length--;
        }

        return result;
    }

    public IMemento Save()
    {
        return new ConcreteMemento(_state);
    }

    public void Restore(IMemento memento)
    {
        _state = memento.GetState();
        Console.WriteLine($"Originator: My state has changed to: {_state}");
    }
}

// 備忘錄介面提供了取得備忘錄和原發器狀態的方法。但在該介面中並未宣告所有的方法,一些方法只在原發器中宣告。
public interface IMemento
{
    string GetName();

    string GetState();

    DateTime GetDate();
}

// Concrete Memento 儲存原發器狀態,並透過原發器實作備份。備忘錄是不可變的,因此,沒有 set 方法。
public class ConcreteMemento : IMemento
{
    private string _state;
    private DateTime _date;

    public ConcreteMemento(string state)
    {
        _state = state;
        _date = DateTime.Now;
    }

    public string GetState()
    {
        return _state;
    }

    public string GetName()
    {
        return $"{_date} / ({_state.Substring(0, 9)})...";
    }

    public DateTime GetDate()
    {
        return _date;
    }
}

// Caretaker 不依賴於具體備忘錄類別。結果,它不會有任何存取原發器狀態的權利,它只能取得備忘錄的中繼資料。
public class Caretaker
{
    private List<IMemento> _mementos = new List<IMemento>();
    private Originator _originator = null;

    public Caretaker(Originator originator)
    {
        this._originator = originator;
    }

    public void Backup()
    {
        Console.WriteLine("\nCaretaker: Saving Originator's state...");
        _mementos.Add(_originator.Save());
    }

    public void Undo()
    {
        if (_mementos.Count == 0)
        {
            return;
        }

        var memento = _mementos.Last();
        _mementos.Remove(memento);

        Console.WriteLine("Caretaker: Restoring state to: " + memento.GetName());
        try
        {
            _originator.Restore(memento);
        }
        catch (Exception)
        {
            Undo();
        }
    }

    public void ShowHistory()
    {
        Console.WriteLine("Caretaker: Here's the list of mementos:");

        foreach (var memento in _mementos)
        {
            Console.WriteLine(memento.GetName());
        }
    }
}

// 用戶端程式碼
class Program
{
    static void Main(string[] args)
    {
        Originator originator = new Originator("Super-duper-super-puper-super.");
        Caretaker caretaker = new Caretaker(originator);

        caretaker.Backup();
        originator.DoSomething();

        caretaker.Backup();
        originator.DoSomething();

        caretaker.Backup();
        originator.DoSomething();

        Console.WriteLine();
        caretaker.ShowHistory();

        Console.WriteLine("\nClient: Now, let's rollback!\n");
        caretaker.Undo();

        Console.WriteLine("\nClient: Once more!\n");
        caretaker.Undo();
    }
}

以上的程式碼中,Originator 持有一些重要的狀態,並且提供了方法去儲存它的狀態到一個備忘錄物件以及從備忘錄物件中恢復它的狀態。Caretaker 負責儲存備忘錄,但是它不能操作備忘錄物件中的狀態。當使用者執行操作時,我們先儲存目前的狀態,然後執行操作。如果使用者後來不滿意新的狀態,他們可以方便地從舊的備忘錄中恢復狀態。

7. 觀察者模式(Observer)

觀察者模式(Observer Pattern)是一種行為型設計模式,當一個物件的狀態發生變化時,依賴它的所有物件都會得到通知並被自動更新。以下是在 C# 中實作觀察者模式的一個簡單範例:

// 抽象觀察者
public interface IObserver
{
    void Update();
}

// 具體觀察者
public class ConcreteObserver : IObserver
{
    private string name;

    public ConcreteObserver(string name)
    {
        this.name = name;
    }

    public void Update()
    {
        Console.WriteLine($"{name} received an update!");
    }
}

// 抽象主題
public interface ISubject
{
    void RegisterObserver(IObserver observer);
    void RemoveObserver(IObserver observer);
    void NotifyObservers();
}

// 具體主題
public class ConcreteSubject : ISubject
{
    private List<IObserver> observers = new List<IObserver>();

    public void RegisterObserver(IObserver observer)
    {
        observers.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        if (observers.Contains(observer))
        {
            observers.Remove(observer);
        }
    }

    public void NotifyObservers()
    {
        foreach (var observer in observers)
        {
            observer.Update();
        }
    }

    public void ChangeState()
    {
        // 觸發狀態變化,通知所有觀察者
        NotifyObservers();
    }
}

在這個程式碼中,IObserver是抽象觀察者,定義了Update方法,ConcreteObserver是具體觀察者,實作了IObserver介面。ISubject是抽象主題,定義了RegisterObserverRemoveObserverNotifyObservers方法,ConcreteSubject是具體主題,實作了ISubject介面。

以下是一個使用這個模式的範例:

class Program
{
    static void Main(string[] args)
    {
        ConcreteSubject subject = new ConcreteSubject();

        subject.RegisterObserver(new ConcreteObserver("Observer 1"));
        subject.RegisterObserver(new ConcreteObserver("Observer 2"));
        subject.RegisterObserver(new ConcreteObserver("Observer 3"));

        subject.ChangeState();

        Console.ReadLine();
    }
}

在這個例子中,我們建立了一個ConcreteSubject物件,並註冊了三個觀察者。然後我們透過ChangeState方法改變了主題的狀態,這會觸發主題通知所有觀察者。

執行流程如下:

  1. 建立一個具體主題物件。
  2. 建立幾個具體觀察者物件,並透過主題的RegisterObserver方法將這些觀察者註冊到主題中。
  3. 透過主題的ChangeState方法改變主題的狀態,這會觸發主題通知所有觀察者。

8. 狀態模式(State)

狀態模式在物件導向程式設計中,是一種允許物件在其內部狀態改變時改變其行為的設計模式。這種類型的設計模式屬於行為型模式。在狀態模式中,我們建立物件表示各種狀態,以及一個行為隨狀態改變而改變的上下文物件。

以下是一個狀態模式的範例。這個範例中,我們將建立一個銀行帳戶,它有兩個狀態:正常狀態(NormalState)和透支狀態(OverdrawnState)。當使用者執行操作(存款和提款)時,帳戶狀態將相應地進行更改。

首先,我們定義一個表示狀態的介面:

public interface IAccountState
{
    void Deposit(Action addToBalance);
    void Withdraw(Action subtractFromBalance);
    void ComputeInterest();
}

然後,我們建立兩個表示具體狀態的類別:

public class NormalState : IAccountState
{
    public void Deposit(Action addToBalance)
    {
        addToBalance();
        Console.WriteLine("Deposit in NormalState");
    }

    public void Withdraw(Action subtractFromBalance)
    {
        subtractFromBalance();
        Console.WriteLine("Withdraw in NormalState");
    }

    public void ComputeInterest()
    {
        Console.WriteLine("Interest computed in NormalState");
    }
}

public class OverdrawnState : IAccountState
{
    public void Deposit(Action addToBalance)
    {
        addToBalance();
        Console.WriteLine("Deposit in OverdrawnState");
    }

    public void Withdraw(Action subtractFromBalance)
    {
        Console.WriteLine("No withdraw in OverdrawnState");
    }

    public void ComputeInterest()
    {
        Console.WriteLine("Interest and fees computed in OverdrawnState");
    }
}

然後,我們建立一個 Context 類別,它使用這些狀態來執行其任務:

public class BankAccount
{
    private IAccountState _state;
    private double _balance;

    public BankAccount(IAccountState state)
    {
        _state = state;
        _balance = 0;
    }

    public void Deposit(double amount)
    {
        _state.Deposit(() => _balance += amount);
        StateChangeCheck();
    }

    public void Withdraw(double amount)
    {
        _state.Withdraw(() => _balance -= amount);
        StateChangeCheck();
    }

    public void ComputeInterest()
    {
        _state.ComputeInterest();
    }

    private void StateChangeCheck()
    {
        if (_balance < 0.0)
            _state = new OverdrawnState();
        else
            _state = new NormalState();
    }
}

現在,你可以建立一個實例並執行一個 Demo 來測試這個狀態模式的程式碼:

public class Program
{
    public static void Main(string[] args)
    {
        var account = new BankAccount(new NormalState());

        account.Deposit(1000); // Deposit in NormalState
        account.Withdraw(2000); // Withdraw in NormalState; No withdraw in OverdrawnState
        account.Deposit(100); // Deposit in OverdrawnState

        account.ComputeInterest(); // Interest and fees computed in OverdrawnState

        Console.ReadKey();
    }
}

這個程式首先在正常狀態下進行存款操作,然後嘗試進行提款操作。由於提款金額超過帳戶餘額,所以帳戶進入透支狀態,並阻止進一步的提款操作。但存款仍然被允許,以使帳戶回歸到正常狀態。計算利息的行為也根據帳戶的狀態變化而變化。

9. 策略模式(Strategy)

策略模式定義了一系列的演算法,並將每一個演算法封裝起來,使得它們可以互相替換。策略模式讓演算法獨立於使用它的客戶而獨立變化。

以下是一個簡單的策略模式的 C# 實作。這個例子中,我們將建立一個排序策略,比如快速排序和氣泡排序,它們實作同一個介面,然後建立一個 Context 類別,它使用這些策略來執行排序操作。

首先,我們定義一個表示排序策略的介面:

public interface ISortStrategy
{
    void Sort(List<int> list);
}

然後,我們建立兩個表示具體策略的類別:

public class QuickSort : ISortStrategy
{
    public void Sort(List<int> list)
    {
        list.Sort();  // Quick sort is in-place but here we are using built-in method
        Console.WriteLine("QuickSorted list ");
    }
}

public class BubbleSort : ISortStrategy
{
    public void Sort(List<int> list)
    {
        int n = list.Count;
        for (int i = 0; i < n - 1; i++)
            for (int j = 0; j < n - i - 1; j++)
                if (list[j] > list[j + 1])
                {
                    // swap temp and list[i]
                    int temp = list[j];
                    list[j] = list[j + 1];
                    list[j + 1] = temp;
                }

        Console.WriteLine("BubbleSorted list ");
    }
}

然後,我們建立一個 Context 類別,它使用這些策略來執行其任務:

public class SortedList
{
    private List<int> _list = new List<int>();
    private ISortStrategy _sortstrategy;

    public void SetSortStrategy(ISortStrategy sortstrategy)
    {
        this._sortstrategy = sortstrategy;
    }

    public void Add(int num)
    {
        _list.Add(num);
    }

    public void Sort()
    {
        _sortstrategy.Sort(_list);

        // Print sorted list
        foreach (int num in _list)
        {
            Console.Write(num + " ");
        }
        Console.WriteLine();
    }
}

現在,你可以建立一個實例並執行一個 Demo 來測試這個策略模式的程式碼:

public class Program
{
    public static void Main(string[] args)
    {
        SortedList sortedList = new SortedList();

        sortedList.Add(1);
        sortedList.Add(5);
        sortedList.Add(3);
        sortedList.Add(4);
        sortedList.Add(2);

        sortedList.SetSortStrategy(new QuickSort());
        sortedList.Sort();  // Output: QuickSorted list 1 2 3 4 5

        sortedList.SetSortStrategy(new BubbleSort());
        sortedList.Sort();  // Output: BubbleSorted list 1 2 3 4 5

        Console.ReadKey();
    }
}

這個程式首先建立了一個未排序的列表,然後它首先使用快速排序策略進行排序,接著又使用氣泡排序策略進行排序。

10. 樣板方法模式(Template Method)

樣板方法模式定義了一個操作中演算法的骨架,將這些步驟延遲到子類別中。樣板方法使得子類別可以不改變演算法的結構即可重新定義該演算法的某些特定步驟。

以下是一個樣板方法模式的範例。這個範例中,我們將建立一個烹飪食物的過程,這個過程有一些固定的步驟(例如準備材料,清理),但是具體的烹飪步驟則取決於具體的食物。

首先,我們定義一個抽象的樣板類別:

public abstract class CookingProcedure
{
    // The 'Template method'
    public void PrepareDish()
    {
        PrepareIngredients();
        Cook();
        CleanUp();
    }

    public void PrepareIngredients()
    {
        Console.WriteLine("Preparing the ingredients...");
    }

    // These methods will be overridden by subclasses
    public abstract void Cook();

    public void CleanUp()
    {
        Console.WriteLine("Cleaning up...");
    }
}

然後,我們建立兩個具體的子類別,它們分別實作了具體的烹飪步驟:

public class CookPasta : CookingProcedure
{
    public override void Cook()
    {
        Console.WriteLine("Cooking pasta...");
    }
}

public class BakeCake : CookingProcedure
{
    public override void Cook()
    {
        Console.WriteLine("Baking cake...");
    }
}

現在,你可以建立一個實例並執行一個 Demo 來測試這個樣板方法模式的程式碼:

public class Program
{
    public static void Main(string[] args)
    {
        CookingProcedure cookingProcedure = new CookPasta();
        cookingProcedure.PrepareDish();

        Console.WriteLine();

        cookingProcedure = new BakeCake();
        cookingProcedure.PrepareDish();

        Console.ReadKey();
    }
}

在這個程式中,我們首先建立了一個CookPasta物件,然後呼叫其PrepareDish方法。然後,我們建立了一個BakeCake物件,再次呼叫其PrepareDish方法。這兩個物件雖然具有不同的Cook方法,但是它們的PrepareDish方法的結構(即演算法的骨架)是相同的。

11. 訪客模式(Visitor)

訪客模式(Visitor Pattern)是一種將演算法與物件結構分離的軟體設計模式。這種模式的基本想法就是透過所謂的"訪客"來改變元素的操作。這樣一來,元素的類別可以用於表示元素結構,而具體的操作則可以在訪客類別中定義。

以下是一個使用 C# 實作的訪客模式範例,包括了詳細的註解和執行流程。

這個範例中有三個主要部分:訪客(IVisitor)、可存取元素(IElement)和元素結構(ObjectStructure)。同時有具體訪客(ConcreteVisitor)和具體元素(ConcreteElement)。

// 訪客介面
public interface IVisitor
{
    void VisitConcreteElementA(ConcreteElementA concreteElementA);
    void VisitConcreteElementB(ConcreteElementB concreteElementB);
}

// 具體訪客 A
public class ConcreteVisitorA : IVisitor
{
    public void VisitConcreteElementA(ConcreteElementA concreteElementA)
    {
        Console.WriteLine($"{concreteElementA.GetType().Name} is being visited by {this.GetType().Name}");
    }

    public void VisitConcreteElementB(ConcreteElementB concreteElementB)
    {
        Console.WriteLine($"{concreteElementB.GetType().Name} is being visited by {this.GetType().Name}");
    }
}

// 具體訪客 B
public class ConcreteVisitorB : IVisitor
{
    public void VisitConcreteElementA(ConcreteElementA concreteElementA)
    {
        Console.WriteLine($"{concreteElementA.GetType().Name} is being visited by {this.GetType().Name}");
    }

    public void VisitConcreteElementB(ConcreteElementB concreteElementB)
    {
        Console.WriteLine($"{concreteElementB.GetType().Name} is being visited by {this.GetType().Name}");
    }
}

// 元素介面
public interface IElement
{
    void Accept(IVisitor visitor);
}

// 具體元素 A
public class ConcreteElementA : IElement
{
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteElementA(this);
    }
}

// 具體元素 B
public class ConcreteElementB : IElement
{
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteElementB(this);
    }
}

// 物件結構
public class ObjectStructure
{
    private List<IElement> _elements = new List<IElement>();

    public void Attach(IElement element)
    {
        _elements.Add(element);
    }

    public void Detach(IElement element)
    {
        _elements.Remove(element);
    }

    public void Accept(IVisitor visitor)
    {
        foreach (var element in _elements)
        {
            element.Accept(visitor);
        }
    }
}

執行流程如下:

  1. 建立具體元素 ConcreteElementA 和 ConcreteElementB 的實例。
  2. 建立物件結構 ObjectStructure 的實例,並將步驟 1 建立的具體元素加入物件結構中。
  3. 建立具體訪客 ConcreteVisitorA 和 ConcreteVisitorB 的實例。
  4. 呼叫物件結構的 Accept 方法,傳入步驟 3 建立的具體訪客,使具體訪客存取物件結構中的所有元素。

以下是一個使用上述程式碼的範例:

public class Program
{
    public static void Main()
    {
        ObjectStructure objectStructure = new ObjectStructure();

        objectStructure.Attach(new ConcreteElementA());
        objectStructure.Attach(new ConcreteElementB());

        ConcreteVisitorA visitorA = new ConcreteVisitorA();
        ConcreteVisitorB visitorB = new ConcreteVisitorB();

        objectStructure.Accept(visitorA);
        objectStructure.Accept(visitorB);

        Console.ReadKey();
    }
}

這個程式會列印出訪客 A 和訪客 B 分別存取具體元素 A 和具體元素 B 的資訊。

技術交流

.NET Core 交流群:737776595

來自 token 的分享

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2026/2/7

AOT使用經驗總結

從專案建立伊始,就應養成良好的習慣,即只要添加了新功能或使用了較新的語法,就及時進行 AOT 發布測試。

繼續閱讀