1. 程式人生 > >Head First設計模式之狀態模式

Head First設計模式之狀態模式

對象狀態 initial fff rst actions arc sof ret %20

一、定義

定義:允許對象在內部狀態改變時改變它的行為, 對象看起來好像修改了它的類。

主要解決:對象的行為依賴於它的狀態(屬性),並且可以根據它的狀態改變而改變它的相關行為。

何時使用:代碼中包含大量與對象狀態有關的條件語句。

如何解決:將各種具體的狀態類抽象出來。

關鍵代碼:通常命令模式的接口中只有一個方法。而狀態模式的接口中有一個或者多個方法。而且,狀態模式的實現類的方法,一般返回值,或者是改變實例變量的值。也就是說,狀態模式一般和對象的狀態有關。實現類的方法有不同的功能,覆蓋接口中的方法。狀態模式和命令模式一樣,也可以用於消除 if...else 等條件選擇語句。

二、結構圖

技術分享

Context類:維護一個ConcreteState子類的一個實例,這個實例定義當前的狀態。

State類:抽象狀態類,定義一個接口以封裝與Context的一個特定狀態相關的行為。

ConcreteStateA,ConcreteStateB,ConcreteStateC類:具體狀態類,每一個子類實現一個與Context的一個狀態相關的行為。

三、適用場景

1、行為隨狀態改變而改變的場景。

2、條件、分支語句的代替者。

四、優缺點

優點: 1、封裝了轉換規則。 2、枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。 3、將所有與某個狀態有關的行為放到一個類中,並且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為。 4、允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。 5、可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。

缺點: 1、狀態模式的使用必然會增加系統類和對象的個數。 2、狀態模式的結構與實現都較為復雜,如果使用不當將導致程序結構和代碼的混亂。 3、狀態模式對"開閉原則"的支持並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需修改對應類的源代碼。

五、實現

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DesignPatterns.StatePattern { class Program { static void Main(string[] args) { // Open a new account Account account = new Account("Jim Johnson"); // Apply financial transactions account.Deposit(500.0); account.Deposit(300.0); account.Deposit(550.0); account.PayInterest(); account.Withdraw(2000.00); account.Withdraw(1100.00); // Wait for user Console.ReadKey(); } } class Account { private State _state; private string _owner; // Constructor public Account(string owner) { // New accounts are ‘Silver‘ by default this._owner = owner; this._state = new SilverState(0.0, this); } // Properties public double Balance { get { return _state.Balance; } } public State State { get { return _state; } set { _state = value; } } public void Deposit(double amount) { _state.Deposit(amount); Console.WriteLine("Deposited {0:C} --- ", amount); Console.WriteLine("Balance = {0:C}", this.Balance); Console.WriteLine("Status = {0}",this.State.GetType().Name); Console.WriteLine(""); } public void Withdraw(double amount) { _state.Withdraw(amount); Console.WriteLine("Withdrew {0:C} --- ", amount); Console.WriteLine(" Balance = {0:C}", this.Balance); Console.WriteLine(" Status = {0}\n", this.State.GetType().Name); } public void PayInterest() { _state.PayInterest(); Console.WriteLine("Interest Paid --- "); Console.WriteLine(" Balance = {0:C}", this.Balance); Console.WriteLine(" Status = {0}\n", this.State.GetType().Name); } } /// <summary> /// The ‘State‘ abstract class /// </summary> abstract class State { protected Account account; protected double balance; protected double interest; protected double lowerLimit; protected double upperLimit; // Properties public Account Account { get { return account; } set { account = value; } } public double Balance { get { return balance; } set { balance = value; } } public abstract void Deposit(double amount); public abstract void Withdraw(double amount); public abstract void PayInterest(); } /// <summary> /// A ‘ConcreteState‘ class /// <remarks> /// Red indicates that account is overdrawn /// </remarks> /// </summary> class RedState : State { private double _serviceFee; // Constructor public RedState(State state) { this.balance = state.Balance; this.account = state.Account; Initialize(); } private void Initialize() { // Should come from a datasource interest = 0.0; lowerLimit = -100.0; upperLimit = 0.0; _serviceFee = 15.00; } public override void Deposit(double amount) { balance += amount; StateChangeCheck(); } public override void Withdraw(double amount) { amount = amount - _serviceFee; Console.WriteLine("No funds available for withdrawal!"); } public override void PayInterest() { // No interest is paid } private void StateChangeCheck() { if (balance > upperLimit) { account.State = new SilverState(this); } } } /// <summary> /// A ‘ConcreteState‘ class /// <remarks> /// Silver indicates a non-interest bearing state /// </remarks> /// </summary> class SilverState : State { // Overloaded constructors public SilverState(State state) : this(state.Balance, state.Account) { } public SilverState(double balance, Account account) { this.balance = balance; this.account = account; Initialize(); } private void Initialize() { // Should come from a datasource interest = 0.0; lowerLimit = 0.0; upperLimit = 1000.0; } public override void Deposit(double amount) { balance += amount; StateChangeCheck(); } public override void Withdraw(double amount) { balance -= amount; StateChangeCheck(); } public override void PayInterest() { balance += interest * balance; StateChangeCheck(); } private void StateChangeCheck() { if (balance < lowerLimit) { account.State = new RedState(this); } else if (balance > upperLimit) { account.State = new GoldState(this); } } } /// <summary> /// A ‘ConcreteState‘ class /// <remarks> /// Gold indicates an interest bearing state /// </remarks> /// </summary> class GoldState : State { // Overloaded constructors public GoldState(State state) : this(state.Balance, state.Account) { } public GoldState(double balance, Account account) { this.balance = balance; this.account = account; Initialize(); } private void Initialize() { // Should come from a database interest = 0.05; lowerLimit = 1000.0; upperLimit = 10000000.0; } public override void Deposit(double amount) { balance += amount; StateChangeCheck(); } public override void Withdraw(double amount) { balance -= amount; StateChangeCheck(); } public override void PayInterest() { balance += interest * balance; StateChangeCheck(); } private void StateChangeCheck() { if (balance < 0.0) { account.State = new RedState(this); } else if (balance < lowerLimit) { account.State = new SilverState(this); } } } }

現實中還有其他很多例子,比如自動販賣機、電梯等。

狀態模式的關鍵在於 狀態變化時引起行為的變化,它是被動的觸發

策略模式的差別在於,它是由外部(client)主動引起行為的變化,可以隨意控制它想要執行的行為。


參考文章:

http://www.cnblogs.com/ywqu/archive/2010/01/26/1656418.html

http://www.runoob.com/design-pattern/state-pattern.html

http://www.cnblogs.com/JsonShare/p/7246915.html

歡迎閱讀本系列文章:Head First設計模式之目錄

Head First設計模式之狀態模式