首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 設計模式無廢話系列(十六):State 狀態模式

     編輯:關於C#

意圖

允許對象在內部狀態改變時改變行為,彷彿修改了其類別。

適用場景

網路書店會員制度:消費達標升級銀/金會員,不同等級享不同折扣。若在 User.BuyBook 中用條件判斷等級折扣,問題:

  • 折扣率常變 → 改 User 類。
  • 新增鑽石/白金會員 → 改 User 類。
  • User 承擔等級判斷、折扣計算等複雜邏輯 → 可維護性差。

引入 State 模式,將對象與狀態分離,狀態轉換與行為交由具體狀態類處理。

完整範例程式碼

using System;
using System.Collections.Generic;
using System.Text;

namespace StateExample
{
    class Program
    {
        static void Main(string[] args)
        {
            User user = new User("zhuye");
            user.BuyBook(2000);
            user.BuyBook(2000);
            user.BuyBook(2000);
            user.BuyBook(2000);
        }
    }

    class User
    {
        public UserLevel UserLevel { get; set; }
        string userName;
        double paidMoney;
        public double PaidMoney => paidMoney;

        public User(string userName)
        {
            this.userName = userName;
            paidMoney = 0;
            UserLevel = new NormalUser(this);
        }

        public void BuyBook(double amount)
        {
            Console.WriteLine($"Hello {userName}, You have paid ${paidMoney}, You Level is {UserLevel.GetType().Name}.");
            double real = UserLevel.CalcRealAmount(amount);
            Console.WriteLine($"You only paid ${real} for this book.");
            paidMoney += real;
            UserLevel.StateCheck();
        }
    }

    abstract class UserLevel
    {
        protected User user;
        public UserLevel(User user) { this.user = user; }
        public abstract void StateCheck();
        public abstract double CalcRealAmount(double amount);
    }

    class DiamondUser : UserLevel
    {
        public DiamondUser(User user) : base(user) { }
        public override double CalcRealAmount(double amount) => amount * 0.7;
        public override void StateCheck() { } // 最高級無上遷
    }
    class GoldUser : UserLevel
    {
        public GoldUser(User user) : base(user) { }
        public override double CalcRealAmount(double amount) => amount * 0.8;
        public override void StateCheck() { if (user.PaidMoney > 5000) user.UserLevel = new DiamondUser(user); }
    }
    class SilverUser : UserLevel
    {
        public SilverUser(User user) : base(user) { }
        public override double CalcRealAmount(double amount) => amount * 0.9;
        public override void StateCheck() { if (user.PaidMoney > 2000) user.UserLevel = new GoldUser(user); }
    }
    class NormalUser : UserLevel
    {
        public NormalUser(User user) : base(user) { }
        public override double CalcRealAmount(double amount) => amount * 0.95;
        public override void StateCheck() { if (user.PaidMoney > 1000) user.UserLevel = new SilverUser(user); }
    }
}

程式碼執行結果

State 模式執行結果

程式碼解析

  • User:Context(環境角色),持有 UserLevel 實例,對外提供 BuyBook。
  • UserLevel:抽象狀態,定義行為介面(CalcRealAmount、StateCheck),持有 Context 參考以便修改其狀態。
  • NormalUser/SilverUser/GoldUser/DiamondUser:具體狀態,實作折扣與升級邏輯。
  • 狀態轉換在具體狀態中執行(StateCheck),形成有序鏈,僅前後狀態產生依賴。若移除 DiamondUser,GoldUser 的 StateCheck 留空即可;未來新增只需改 GoldUser。
  • 實務可在抽象狀態引入 Template Method:對外公開模板方法,內部呼叫行為與轉換方法,具體行為仍由子類實現。

何時採用

  • 代碼角度:類別有多狀態,內部以條件判斷狀態執行不同行為 → 封裝為狀態類。
  • 應用角度:希望將狀態轉換與狀態相關行為委派給具體狀態類。

實作要點

  • Context 持有 State 實例;State 持有 Context 實例以修改狀態。
  • 狀態間依賴可透過外部配置轉換規則表消除。
  • State vs Strategy:前者行為由條件決定、自動遷移;後者行為由客戶端選擇、可隨時替換。

注意事項

  • 狀態類過多增加系統負擔,可考慮將狀態實現為無狀態 Flyweight,額外狀態由 Context 統一管理。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved