Saladpuk.com
🏆 เนื้อหาหลัก
🏆 เนื้อหาหลัก
  • 💖สลัดผัก
  • 📰มีอะไรใหม่บ้าง
    • 2020
      • 2020-11
      • 2020-10
      • 2020-09
      • 2020-08
      • 2020-03
      • 2020-02
      • 2020-01
    • 2019
      • 2019-12
      • 2019-11
      • 2019-10
      • 2019-09
      • 2019-08
  • 🤔อ่านเรื่องไรดี ?
  • มือใหม่หัดเขียนโค้ด
    • 👶เขียนโค้ดด้วยภาษา C#
      • เกิดมาไม่เคยเขียนโค้ดมาก่อนเบย
      • 👶พื้นฐาน
        • 1.โปรแกรมที่ต้องลง
        • 2.โครงสร้างของโค้ด
        • 3.ชนิดของข้อมูล
        • 4.การสร้างตัวแปร
        • 5.คำสั่งพื้นฐาน
        • 6.การแปลงข้อมูล
        • 7.การเปรียบเทียบค่า
        • 8.การตัดสินใจด้วย IF statements
        • 9.การตัดสินใจด้วย Switch statements
        • 10.การทำงานซ้ำๆด้วย While
        • 11.การทำงานซ้ำๆด้วย Do While
        • 12.การทำงานซ้ำๆด้วย For
        • 13.การแก้โจทย์จากรูป
        • 14.มารู้จักกับ Array กัน
      • 🧑ระดับกลาง
        • 15.Value type vs Reference type
        • 16.ลดงานซ้ำๆด้วย Method
        • 17.มารู้จักกับ Class & Field กัน
        • 18.มารู้จักกับ Constructor กันบ้าง
        • 19.มาเขียน Method ใน Class กัน
        • 20.มารู้จักกับ Property กัน
        • 21.ลองใช้คลาสแบบจริงจังบ้าง
        • 22.การสืบทอด Inheritance
        • 23.Polymorphism
        • 24.Abstract Class
        • 25.Interface
        • 26.Namespace
        • 27.Enum
        • 28.Exception handler
        • 29.ลงลึกกับ string
        • 30.StringBuilder เพื่อนคู่ string
      • 👨⏳ระดับสูง
        • Generic
        • Delegates
        • Action & Func
        • Lambda expression
        • LINQ
        • พระคัมภีร์การใช้คำสั่ง LINQ
      • 💡Tips
        • 💡C# version 8.0
        • 💡Boxing & Unboxing
    • 👶Algorithm
      • 👾Algorithm Big-O
      • 👽Algorithm P & NP
    • 👦OOP
      • 💖Abstraction
      • 💖Encapsulation
      • 🏆Abstraction & Encapsulation
      • 💖Inheritance
      • 💖Polymorphism
      • 🏆Inheritance & Polymorphism
      • 📝ลองเขียน OOP ดูดิ๊
      • 👑OOP + Power of Design
      • 🥰เทคนิคในการออกแบบ
    • 👶บทสรุปฐานข้อมูล
      • เก็บรูปในฐานข้อมูล
      • Database indexing
      • การลบข้อมูล
    • 👦Communication Patterns
    • 👦Design Patterns
      • 🤰Creational Patterns
        • 🏭Factory Method
        • 🏭Abstract Factory
        • ☝️ Singleton Pattern
        • 🏗️ Builder Pattern
        • 🎎Prototype Pattern
      • 🧱Structural Patterns
        • 🔌Adapter Pattern
        • 📪Proxy Pattern
  • Puzzle
    • 🧠Challenges
      • 🐴Google ม้า 25 ตัว
      • 🌉Amazon เสา 2 ต้น
      • 🥇ทองเก๊
      • 💊ยาต้านโควิด
      • 🎩CP หมวก 5 ใบ
      • 🧓Einstein's Riddle 01
  • พื้นฐานที่ควรต้องรู้
    • 🐳Docker
      • 📦Docker Containers
      • 🃏Docker Exercise 01
      • 🛠️ Docker Tools
      • 🗃️ Docker Registry
      • 🖼️ Container Image
      • 📢Docker Push
      • 🔄WSL
    • 👶Clean Code
      • 🧓Uncle Bob - Clean Code
      • 🧓Uncle Bob - Comments
      • 🧓Uncle Bob - Naming
      • 🧓Uncle Bob - Mindset
      • 🧓Uncle Bob - TDD
    • 👶Code Smells
    • 👶สิ่งที่คนเขียนโค้ดมักเข้าใจผิด
    • 👶AI พื้นฐาน
    • 👶Git พื้นฐาน
      • Git branching strategy
    • 👶Cloud พื้นฐาน
    • 👶UML พื้นฐาน
      • Activity Diagram
      • Class Diagram
      • Sequence Diagram
      • Use case Diagram
      • บทสรุปการใช้ UML
    • 👶Data Scientist
      • การเลือก Algorithms ให้ AI (1/5)
      • การเตรียมข้อมูลให้ AI (2/5)
      • หลักการตั้งคำถามให้ AI (3/5)
      • แฉความลับของ AI Model (4/5)
      • หัดเขียน AI จาก AI ของคนอื่น (5/5)
    • 👶DevOps พื้นฐาน
    • 👶Docker ขั้นพื้นฐาน
      • Image and Container
      • แชร์ Docker Image ที่สร้างไว้
    • 👶Microservices พื้นฐาน
      • Microservices ที่ดีมีลักษณะยังไง
      • Microservices Tips
      • จาก Monolith สู่ Microservices
    • 👶ความรู้พื้นฐานในการทำเว็บ
    • 👦Bottlenecks of Software
      • หัวใจที่สำคัญที่สุดของฐานข้อมูล
    • 👦Agile Methodology
      • Agile in a Nutshell
      • Software Development Life Cycle
      • Code Review
    • 👦Security พื้นฐาน
      • การเก็บรหัสผ่านที่ถูกต้อง
      • Security in actions
        • Hash function
      • Security Principles
      • 😎The Matrix 1
      • 😎The Matrix 2
      • HTTPS in a nutshell
    • 👦SOLID Design Principles
      • มารู้จักกับ SOLID กันดีกว่า
      • Single-Responsibility Principle
      • Open/Closed Principle
      • Liskov Substitution Principle
      • Interface Segregation Principle
      • Dependency-Inversion Principle
  • Cloud Computing
    • 👶Microsoft Azure 101
      • สมัคร Microsoft Azure
      • รู้จักกับ Resource Groups
      • สร้างเว็บตัวแรกกัน
      • สร้าง Virtual Machine กัน
      • ประเภทของคลาว์เซอร์วิส
      • มาสร้าง Logic App กัน
      • มาสร้าง Function App กัน
      • คลาว์คิดเงินยังไง ?
      • Cloud Native
      • Guideline for Cloud scaling
      • Auto Scaling
    • 👶Azure App Services
    • 👶App Service Plan
    • 👶Azure Storage
      • Blob storage
        • ลองสร้างที่เก็บไฟล์กันเลย
        • เข้าใจ Blob storage ให้มากขึ้น
        • ลองเขียนโค้ดอัพโหลดไฟล์กันบ้าง
        • สร้างเว็บจากที่ฝากไฟล์บนคลาว์
    • 👶Azure Bot Service
      • Bot เข้าใจเราได้ยังไงกันนะ
    • 👶Azure Cognitive Services
      • การสร้าง Cognitive Services
      • การ Login ด้วยใบหน้า
      • อ่านลายมือจากรูปเป็นตัวอักษร (OCR)
      • เขียน AI แยกของต่างๆทำยังไง?
      • เขียนแอพ ทายอายุ บอกเพศ ง่ายจิ๊ดเดียว
      • เขียนแอพให้ AI อธิบายรูปเป็นภาษาคน
    • 👶Machine Learning Studio
      • มาสร้าง AI ของแท้ตัวแรกของเรากัน
      • สร้าง AI ตัดสินใจอนุมัติบัตรเครดิต 💳
      • ลองเรียกใช้ AI ของเรากัน
    • 👶Azure Service Fabric
      • สร้าง Service Fabric กัน
    • 👶Blockchain
      • Blockchain ทำงานยังไง ?
      • Consensus Algorithm คืออะไร ?
      • สร้าง Blockchain ใช้เองกัน !
      • หัดเขียน Smart Contract กัน
    • 👶Power BI
    • 👶Azure Web App
      • เซิฟเวอร์บนคลาว์ ราคา? ต่าง?
    • 👶Azure DevOps
      • เล่น Azure DevOps กัน
      • เล่นกับ Repository
      • ลองทำ Continuous Integration (CI)
      • ลองทำ Continuous Delivery (CD)
      • เล่น Kanban Board
    • 🤠Cloud Playground
      • การป้องกันความลับหลุดตอนที่ 1
      • การป้องกันความลับหลุดตอนที่ 2
      • การป้องกันความลับหลุดตอนที่ 3
      • การป้องกันความลับหลุดตอนจบ
  • Software Testing
    • 👦Test-First Design
    • 👦Test-Driven Development
      • 1.มารู้จักกับ TDD กันดีกว่า
      • 2.Test cases เขาเขียนกันยังไงนะ
      • 3.เครื่องมือในการทดสอบ
      • 4.การใช้ Theory และ InlineData
      • 5.โค้ดที่ทดสอบได้
      • 6.Mantra of TDD
      • 7.Functional & None-Functional testing
      • 8.Manual vs Automation testing
      • 9.Automation Frameworks in .NET
      • 10.Mock Framework
      • 11.มาเรียนการใช้ Moq กันเถอะ
      • 12.สรุป
  • Web
    • 👦Web API
      • 1.Web API คืออะไร
      • 2.ติดตั้ง .NET Core SDK
      • 3.สร้าง Web API ตัวแรกกัน
      • 4.Verbs
      • 5.Swagger เพื่อคู่ API
      • 6.การใช้ Model
      • 7.เรียก Web API ผ่าน Postman
      • 8.มาจัดกลุ่ม API กัน (1/2)
      • 9.มาจัดกลุ่ม API กัน (2/2)
  • Software Design
    • 🤴Design Patterns
      • 🦈Creational patterns
        • Abstract Factory
        • Builder
        • Factory Method
        • Prototype
        • Singleton
      • 🦈Structural patterns
        • Adapter
        • Bridge
        • Decorator
        • Facade
        • Proxy
      • 🦈Behavioral patterns
        • Chain of Responsibility
        • Command
        • Iterator
        • Mediator
        • Memento
        • Observer
        • State
        • Strategy
        • Template Method
        • Visitor
Powered by GitBook
On this page
  • State
  • 🎯 เป้าหมายของ pattern นี้
  • ✌ หลักการแบบสั้นๆ
  • 😢 ปัญหา
  • 😄 วิธีแก้ไข
  • 📌 โครงสร้างของ pattern นี้
  • 🛠 ตัวอย่างการนำไปใช้งาน
  • 👍 ข้อดี
  • 👎 ข้อเสีย
  • ‍‍📝 Code ตัวอย่าง

Was this helpful?

Export as PDF
  1. Software Design
  2. Design Patterns
  3. Behavioral patterns

State

PreviousObserverNextStrategy

Last updated 5 years ago

Was this helpful?

State

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

🎯 เป้าหมายของ pattern นี้

สามารถเปลี่ยนรูปแบบการทำงานของ object ตามสถานะของมันได้

✌ หลักการแบบสั้นๆ

  1. แยก state ต่างๆออกมาเป็น class ต่างหาก และรวมถึง logic ของมันด้วย

  2. ให้ context ทำงานกับ interface แทนที่จะทำงานกับ concreate state

😢 ปัญหา

ในเรื่องนี้จะคล้ายๆกับ (แต่ไม่รู้เรื่องก็ไม่เป็นไร คิดซะว่าเรากำลังจะไปดูหนัง The imitation game ละกัน ซึ่งเจ้าเรื่องนี้เป็นจุดกำหนดคอมพิวเตอร์และ State machine นั่นเอง)

แนวคิดของ Finite-State Machine มีอยู่ว่า ในโปรแกรมอะไรก็แล้วแต่มันจะมีสถานะต่างๆ เช่น โปรแกรมปิด, โปรแกรมเปิด, โปรแกรมกำลังทำงาน, โปรแกรมถูกหยุด โดยของพวกนี้เราเรียกมันว่า สถานะ หรือ (State) ซึ่งมันมีกฎอยู่ว่า

  1. ตัวโปรแกรมสามารถเปลี่ยน State ได้ถ้าตรงกับเงื่อนไขที่กำหนด

  2. ในแต่ละช่วงเวลามันจะต้องเป็นแค่ State ใด state หนึ่งเท่านั้น ไม่สามารถเป็น 2 state ได้พร้อมกัน

  3. State ในโปรแกรมจะมีจำนวนจำกัด อาจจะมีเป็นร้อยๆพันๆก็แล้วแต่ แต่สุดท้ายเราก็สามารถนับ Unique state ได้แน่นอนนั่นเอง

ตามรูปด้านล่าง

ซึ่งจากหลักการที่ว่ามา ก็เหมือนกับ object ในโค้ดของเรา เพราะตัวมันก็มี State ที่สามารถเปลี่ยนไปมาได้ เพื่อให้เห็นภาพขอสมมุติว่าเรามี class ที่ชื่อ Document ตัวนึง ซึ่งเจ้า object ของมันก็สามารถเปลี่ยนเป็น state ต่างๆได้ เช่น กำลังร่างเอกสาร (Draft) กำลังตรวจสอบ (Moderation) ถูกตีพิมพ์แล้ว (Published) ประมาณนี้

คราวนี้สมมุติต่อว่าเจ้า Document ของเรามันมี method 1 ตัวที่ชื่อว่า Publish มีหน้าที่ไว้เผยแพร่เอกสาร ซึ่งถ้าเรียกใช้งานเจ้า method นี้ มันจะทำงานยังไงขึ้นอยู่กับว่าตอนนี้เป็น State อะไร ตามนี้

  • อยู่ใน state กำลังร่างเอกสาร (Draft) มันจะเปลี่ยน state เป็น กำลังตรวจสอบ (Moderation) แต่ถ้าคนที่กดเป็น Admin มันจะเปลี่ยน state เป็น ถูกตีพิมพ์แล้ว (Published)

  • อยู่ใน state กำลังร่างเอกสาร (Moderation) ถ้าคนที่กดจะต้องเป็น Admin มันจะเปลี่ยน state เป็น ถูกตีพิมพ์แล้ว (Published)แต่ถ้าไม่ใช่ มันจะไม่ทำอะไรเลย

  • อยู่ใน state ถูกตีพิมพ์แล้ว (Published) มันจะไม่ทำอะไรเลย

และถ้าเอกสารหมดอายุ มันก็จะกลับไปอยู่ใน state กำลังร่างเอกสาร (Draft) ตาม State diagram ด้านล่างเลย

จากที่ว่ามาน่าจะพอเห็นภาพละว่า เวลาที่เราเขียนโปรแกรมเพื่อเปลี่ยนสถานะ (State) นั้น โค้ดของเราจะประกอบไปด้วยพวก Condition statement (if, switch) พวกนี้ ซึ่งในโค้ดที่เราเขียนก็จะเป็นการเปลี่ยนค่า field ซะส่วนใหญ่ ตามโค้ดด้านล่างนี้

public void Publish()
{
    switch(state)
    {
        case "Draft":
        {
            if(IsAdmin)
            {
                state = "Publish";
            }
            else
            {
                state = "Moderation"
            }
            break;
        }
        case "Moderation":
        {
            if(IsAdmin)
            {
                state = "Publish";
            }
            break;
        }
        case "Published":
        default:
            break;
    }
}

จากโค้ดตัวอย่างเราจะเห็นปัญหาตัวหนึ่งนั่นคือ ทุกครั้งที่เราเพิ่มสถานะ (State) เข้ามา มันจะทำให้โค้ดในการตรวจสอบเงื่อนไขใหญ่ขึ้นไปเรื่อยๆ หรือมีการเปลี่ยนเงื่อนไขของ state ขึ้นมา เราก็ต้องมานั่งแก้โค้ดพวกนี้ทุกครั้งเช่นกัน และยิ่งแย่ไปกว่านั้น ถ้าโค้ดเรามีการตรวจสอบ state พวกนี้ในจุดอื่นๆ เราก็ต้องตามไปไล่แก้จุดพวกนั้นด้วยเช่นกัน

โดยปรกติถ้าเราเพิ่ม feature เข้าไปเรื่อยๆ โปรแกรมของเราก็จะมี state เข้ามาเพิ่มเรื่อยๆเช่นกัน แล้วเราจะแก้ปัญหาพวกนี้ยังไงกันดีน๊าาา (วันนี้ไม่มีกาวนะ เพราะผมรีบ)

😄 วิธีแก้ไข

State pattern แนะนำให้เราแยก state ทั้งหมดภายใน object ออกไปเป็น class ของมันเอง และย้ายเงื่อนไขต่างๆของมันไปอยู่ภายใน class ใหม่พวกนั้นด้วย

ถัดมาให้ object ตัวหลักของเรา reference ไปยัง state class พวกนั้น เพื่อจะได้ไม่มีเงื่อนไขไปอยู่ใน object หลักของเรา ตามรูป

คราวนี้เวลาที่เราอยากจะเปลี่ยน state ของเจ้า object หลักของเรา (object หลักเราเรียกมันว่า Context) เราก็แค่เรียกให้เจ้า state object ของเราไปทำงาน เพื่อตรวจสอบเงื่อนไขต่างๆของมัน ซึ่งถ้ามันถูกต้องตามเงื่อนไขเจ้า state object ก็จะทำการเปลี่ยน state object ตัวอื่นมาแทนที่มันให้กับ object หลักนั่นเอง

📌 โครงสร้างของ pattern นี้

อธิบาย 1.Context - จะอ้างถึง concrete state object และจะเรียกใช้ concreate state objects ผ่าน interface 2.State interface - interface กลางสำหรับสร้าง concreate state 3.Concrete States - เป็นตัวที่ใช้ทำงานใน state ที่มันรับผิดชอบที่แท้จริง และในบางที state object อาจจะ reference กลับไปหา context object ด้วย 4.ทั้ง context และ concreate states สามารถกำหนด state ให้กับ context ได้

🛠 ตัวอย่างการนำไปใช้งาน

ในรอบนี้ผมจะลองเขียนโปรแกรมตู้ซื้อสินค้าอัตโนมัติละกัน โดยตู้จะมีสถานะแค่ 2 สถานะคือ ซื้อสินค้าได้ และ ซื้อสินค้าไม่ได้

ส่วนบนตู้มีแค่ ช่องให้หยอดเหรียญเข้าไป และ มีปุ่มให้กดซื้อสินค้า

โดยผมกำหนดว่าสินค้าในตู้ทุกอย่างชิ้นละ 7 บาทละกัน

ปะไปลองดูโค้ดกันเลยว่า เราจะจัดการกับ state 2 แบบนี้ยังไง โดยที่โปรแกรมจะรองรับ state ต่างๆเข้ามาเรื่อยๆได้ โดยที่เจ้า context เราจะไม่เปลี่ยนแปลงอะไรเลย

👍 ข้อดี

  • รองรับ state ในอนาคตที่จะเพิ่ม/ลด ก็จะไม่มีผลกับโค้ดของ context

  • ถูกหลัก Single Responsibility Principle

  • ถูกหลัก Open/Closed Principle

👎 ข้อเสีย

  • อย่าใช้ pattern นี้ถ้าโปรแกรมของเรามี state แค่นิดๆหน่อยๆเท่านั้น ไม่งั้นโค้ดจะซับซ้อนโดยไม่จำเป็น

‍‍📝 Code ตัวอย่าง

using System;

// State Interface
interface IAutoMachineState
{
    void InsertCoin(int amount);
    void Buy();
}

// Context
class AutoMachine
{
    public int CurrentCoins { get; set; }
    private IAutoMachineState currentState;

    public AutoMachine()
    { 
        currentState = new NothingAvailable(this);
         Console.WriteLine("กำหนดสถานะเริ่มต้นเป็น - ไม่สามารถซื้ออะไรได้เลย");
    }

    public void ChangeState(IAutoMachineState state)
        => currentState = state;

    public void InsertCoin(int amount)
    { 
        CurrentCoins += amount;
        currentState.InsertCoin(amount);
    }

    public void Buy()
        => currentState.Buy();
}

// Concrete state
class NothingAvailable : IAutoMachineState
{
    private AutoMachine context;

    public NothingAvailable(AutoMachine context)
        => this.context = context;

    public void InsertCoin(int amount)
    {
        const int MinimumCoinsForBuySomthing = 7;
        if(context.CurrentCoins >= MinimumCoinsForBuySomthing)
        {
            Console.WriteLine("เปลี่ยนสถานะเป็น - สามารถซื้ออะไรก็ได้");
            context.ChangeState(new SomethingAvailable(context));
        }
    }

    public void Buy()
    {
        Console.WriteLine("ไม่สามารถซื้ออะไรได้");
    }
}
class SomethingAvailable : IAutoMachineState
{
    private AutoMachine context;

    public SomethingAvailable(AutoMachine context)
        => this.context = context;

    public void InsertCoin(int amount) { }

    public void Buy()
    {
        const int MinimumCoinsForBuySomthing = 7;
        context.CurrentCoins -= MinimumCoinsForBuySomthing;
        Console.WriteLine("ซื้อสินค้าไปแล้ว");

        if(context.CurrentCoins < MinimumCoinsForBuySomthing)
        {
            context.ChangeState(new NothingAvailable(context));
            Console.WriteLine("เปลี่ยนสถานะเป็น - ไม่สามารถซื้ออะไรได้เลย");
        }
    }
}

// Client
class Program
{
    static void Main()
    {
        var machine = new AutoMachine();
        Console.WriteLine("ใส่เงิน 5 บาทเข้าไปในตู้");
        machine.InsertCoin(5);
        Console.WriteLine("กดซื้อของ");
        machine.Buy();
        Console.WriteLine("ใส่เงิน 3 บาทเข้าไปในตู้");
        machine.InsertCoin(3);
        Console.WriteLine("กดซื้อของ");
        machine.Buy();
    }
}

Output

กำหนดสถานะเริ่มต้นเป็น - ไม่สามารถซื้ออะไรได้เลย
ใส่เงิน 5 บาทเข้าไปในตู้
กดซื้อของ
ไม่สามารถซื้ออะไรได้
ใส่เงิน 3 บาทเข้าไปในตู้
เปลี่ยนสถานะเป็น - สามารถซื้ออะไรก็ได้
กดซื้อของ
ซื้อสินค้าไปแล้ว
เปลี่ยนสถานะเป็น - ไม่สามารถซื้ออะไรได้เลย

State pattern จะคล้ายกับ แต่จะต่างกันที่เจ้า state pattern เวลาที่มันจะเปลี่ยน state มันจะรู้จักกับ state อื่นๆ แต่ในขณะที่เจ้า strategy จะไม่รู้จัก strategy อื่นนั่นเอง

🤴
🦈
Strategy pattern
Finite-State Machine
img
img
img
img
img