1. 程式人生 > >設計模式(19)狀態模式

設計模式(19)狀態模式

模式介紹

狀態模式旨在允許物件在其內部狀態改變時改變其自身行為。

示例

我們以烤牛排為例,生熟程度分為:

  • Uncooked (not safe to eat)
  • Rare
  • Medium-Rare (mid-rare)
  • Medium
  • Medium-Well (mid-well)
  • Well done

簡單來說就是從生到熟的幾個檔次。

抽象的狀態類:

/// <summary>
/// The State abstract class
/// </summary>
abstract class Doneness
{
    protected Steak steak;
    protected double currentTemp;
    protected double lowerTemp;
    protected double upperTemp;
    protected bool canEat;

    public Steak Steak
    {
        get { return steak; }
        set { steak = value; }
    }

    public double CurrentTemp
    {
        get { return currentTemp; }
        set { currentTemp = value; }
    }

    public abstract void AddTemp(double temp);
    public abstract void RemoveTemp(double temp);
    public abstract void DonenessCheck();
}

具體的狀態類:

/// <summary>
/// A Concrete State class.
/// </summary>
class Uncooked : Doneness
{
    public Uncooked(Doneness state)
    {
        currentTemp = state.CurrentTemp;
        steak = state.Steak;
        Initialize();
    }

    private void Initialize()
    {
        lowerTemp = 0;
        upperTemp = 130;
        canEat = false;
    }

    public override void AddTemp(double amount)
    {
        currentTemp += amount;
        DonenessCheck();
    }

    public override void RemoveTemp(double amount)
    {
        currentTemp -= amount;
        DonenessCheck();
    }

    public override void DonenessCheck()
    {
        if (currentTemp > upperTemp)
        {
            steak.State = new Rare(this);
        }
    }
}

/// <summary>
/// A 'ConcreteState' class.
/// </summary>
class Rare : Doneness
{
    public Rare(Doneness state) : this(state.CurrentTemp, state.Steak)
    {
    }

    public Rare(double currentTemp, Steak steak)
    {
        this.currentTemp = currentTemp;
        this.steak = steak;
        canEat = true; //We can now eat the steak
        Initialize();
    }

    private void Initialize()
    {
        lowerTemp = 130;
        upperTemp = 139.999999999999;
        canEat = true;
    }

    public override void AddTemp(double amount)
    {
        currentTemp += amount;
        DonenessCheck();
    }

    public override void RemoveTemp(double amount)
    {
        currentTemp -= amount;
        DonenessCheck();
    }

    public override void DonenessCheck()
    {
        if (currentTemp < lowerTemp)
        {
            steak.State = new Uncooked(this);
        }
        else if (currentTemp > upperTemp)
        {
            steak.State = new MediumRare(this);
        }
    }
}

/// <summary>
/// A Concrete State class
/// </summary>
class MediumRare : Doneness
{
    public MediumRare(Doneness state) : this(state.CurrentTemp, state.Steak)
    {
    }

    public MediumRare(double currentTemp, Steak steak)
    {
        this.currentTemp = currentTemp;
        this.steak = steak;
        canEat = true;
        Initialize();
    }

    private void Initialize()
    {
        lowerTemp = 140;
        upperTemp = 154.9999999999;
    }

    public override void AddTemp(double amount)
    {
        currentTemp += amount;
        DonenessCheck();
    }

    public override void RemoveTemp(double amount)
    {
        currentTemp -= amount;
        DonenessCheck();
    }

    public override void DonenessCheck()
    {
        if (currentTemp < 0.0)
        {
            steak.State = new Uncooked(this);
        }
        else if (currentTemp < lowerTemp)
        {
            steak.State = new Rare(this);
        }
        else if (currentTemp > upperTemp)
        {
            steak.State = new Medium(this);
        }
    }
}

/// <summary>
/// A Concrete State class
/// </summary>
class Medium : Doneness
{
    public Medium(Doneness state) : this(state.CurrentTemp, state.Steak)
    {
    }

    public Medium(double currentTemp, Steak steak)
    {
        this.currentTemp = currentTemp;
        this.steak = steak;
        canEat = true;
        Initialize();
    }

    private void Initialize()
    {
        lowerTemp = 155;
        upperTemp = 169.9999999999;
    }

    public override void AddTemp(double amount)
    {
        currentTemp += amount;
        DonenessCheck();
    }

    public override void RemoveTemp(double amount)
    {
        currentTemp -= amount;
        DonenessCheck();
    }

    public override void DonenessCheck()
    {
        if (currentTemp < 130)
        {
            steak.State = new Uncooked(this);
        }
        else if (currentTemp < lowerTemp)
        {
            steak.State = new MediumRare(this);
        }
        else if (currentTemp > upperTemp)
        {
            steak.State = new WellDone(this);
        }
    }
}

/// <summary>
/// A Concrete State class
/// </summary>
class WellDone : Doneness //aka Ruined
{
    public WellDone(Doneness state) : this(state.CurrentTemp, state.Steak)
    {
    }

    public WellDone(double currentTemp, Steak steak)
    {
        this.currentTemp = currentTemp;
        this.steak = steak;
        canEat = true;
        Initialize();
    }

    private void Initialize()
    {
        lowerTemp = 170;
        upperTemp = 230;
    }

    public override void AddTemp(double amount)
    {
        currentTemp += amount;
        DonenessCheck();
    }

    public override void RemoveTemp(double amount)
    {
        currentTemp -= amount;
        DonenessCheck();
    }

    public override void DonenessCheck()
    {
        if (currentTemp < 0)
        {
            steak.State = new Uncooked(this);
        }
        else if (currentTemp < lowerTemp)
        {
            steak.State = new Medium(this);
        }
    }
}

上下文類-牛排:

/// <summary>
/// The Context class
/// </summary>
class Steak
{
    private Doneness _state;
    private string _beefCut;

    public Steak(string beefCut)
    {
        _cook = beefCut;
        _state = new Rare(0.0, this);
    }

    public double CurrentTemp
    {
        get { return _state.CurrentTemp; }
    }

    public Doneness State
    {
        get { return _state; }
        set { _state = value; }
    }

    public void AddTemp(double amount)
    {
        _state.AddTemp(amount);
        Console.WriteLine("Increased temperature by {0} degrees.", amount);
        Console.WriteLine(" Current temp is {0}", CurrentTemp);
        Console.WriteLine(" Status is {0}", State.GetType().Name);
        Console.WriteLine("");
    }

    public void RemoveTemp(double amount)
    {
        _state.RemoveTemp(amount);
        Console.WriteLine("Decreased temperature by {0} degrees.", amount);
        Console.WriteLine(" Current temp is {0}", CurrentTemp);
        Console.WriteLine(" Status is {0}", State.GetType().Name);
        Console.WriteLine("");
    }
}

客戶端呼叫:

static void Main(string[] args)
{
    //Let's cook a steak!
    Steak account = new Steak("T-Bone");

    // Apply temperature changes
    account.AddTemp(120);
    account.AddTemp(15);
    account.AddTemp(15);
    account.RemoveTemp(10); //Yes I know cooking doesn't work this way, bear with me.
    account.RemoveTemp(15);
    account.AddTemp(20);
    account.AddTemp(20);
    account.AddTemp(20);

    Console.ReadKey();
}

總結

狀態模式允許物件的行為隨著其內部狀態的改變而改變,並且它通過使物件的狀態與物件本身分開來完成這一任務。 因此,狀態可以為物件實現它們自己的行為,並且物件可以對其內部狀態改變作出“反應”。

原始碼

原文