ข้ามไปยังเนื้อหา

State

State ยอมให้ object เปลี่ยนแปลงพฤติกรรมเมื่อสถานะภายในของตัวเองเปลี่ยน แต่ละสถานะกลายเป็น object ของตัวเอง และ context มอบหมายงานให้สถานะใดก็ตามที่ถืออยู่ในขณะนั้น เมื่อมองจากภายนอก context ดูเหมือนเปลี่ยน class ในขณะรัน

ลองพิจารณาออเดอร์ที่เคลื่อนผ่าน draft, paid, shipped และ delivered แต่ละแอ็กชัน ได้แก่ pay, ship, deliver, cancel ถูกอนุญาตในบางสถานะและถูกห้ามในบางสถานะ การ implement แบบไร้เดียงสาคือ string สถานะกับพงรกของเงื่อนไขในทุก method คือ “ถ้าสถานะเป็น draft ทำสิ่งนี้ ไม่งั้นถ้า paid ทำสิ่งนั้น ไม่งั้นโยน error” กฎสำหรับแต่ละสถานะถูกป้ายเลอะอยู่ทั่วทุก method และการเพิ่มสถานะหมายถึงการต้องกลับไปดูทุก method อีกครั้ง

ความผันแปรในที่นี้คือ พฤติกรรมต่อสถานะ และการเปลี่ยนผ่านระหว่างสถานะ State รวบรวมพฤติกรรมทั้งหมดสำหรับหนึ่งสถานะไว้ใน object เดียว context ถือ reference ไปยังสถานะปัจจุบันและส่งต่อการเรียกไปยังสถานะนั้น แต่ละสถานะรู้ว่าตัวเองอนุญาตอะไรและจะเคลื่อนไปยังสถานะใดต่อไป การเพิ่มสถานะคือการเพิ่ม class และกฎการเปลี่ยนผ่านก็อยู่ติดกับพฤติกรรมที่ควบคุม

classDiagram
  class Order {
    -state: OrderState
    +pay()
    +ship()
    +status() string
  }
  class OrderState {
    <<interface>>
    +pay(order)
    +ship(order)
    +name() string
  }
  class DraftState {
    +pay(order)
    +ship(order)
  }
  class PaidState {
    +pay(order)
    +ship(order)
  }
  Order o--> OrderState
  OrderState <|.. DraftState
  OrderState <|.. PaidState
Order มอบหมายงานให้ OrderState ปัจจุบันของตัวเอง ซึ่งตัดสินว่าอะไรอนุญาตและสถานะใดมาต่อไป
  • State — interface ที่ประกาศแอ็กชันที่ผันแปรตามสถานะ
  • Concrete State — object สถานะหนึ่งตัว implement แอ็กชันที่อนุญาตและกระตุ้นการเปลี่ยนผ่านไปยังสถานะอื่น
  • Context — ถือ reference ไปยังสถานะปัจจุบันและมอบหมายทุกการเรียกที่ขึ้นกับสถานะให้สถานะนั้น พร้อมเปิดเผยวิธีให้สถานะสลับ context ไปยังสถานะใหม่
  • Client — ขับเคลื่อน context โดยการเรียกแอ็กชันของตัวเอง ไม่จัดการสถานะโดยตรง

state machine ของออเดอร์ที่แต่ละสถานะตัดสินว่า pay และ ship ทำอะไรและสถานะใดมาต่อไป เปรียบเทียบกับ Strategy ที่ client เลือกอัลกอริทึม ส่วนที่นี่สถานะเลือกตัวต่อจากตัวเอง

interface OrderState {
name(): string;
pay(order: Order): void;
ship(order: Order): void;
}
class Order {
private state: OrderState = new DraftState();
setState(s: OrderState): void {
this.state = s;
}
status(): string {
return this.state.name();
}
pay(): void {
this.state.pay(this);
}
ship(): void {
this.state.ship(this);
}
}
class DraftState implements OrderState {
name(): string {
return 'draft';
}
pay(order: Order): void {
order.setState(new PaidState());
}
ship(_order: Order): void {
throw new Error('cannot ship an unpaid order');
}
}
class PaidState implements OrderState {
name(): string {
return 'paid';
}
pay(_order: Order): void {
throw new Error('already paid');
}
ship(order: Order): void {
order.setState(new ShippedState());
}
}
class ShippedState implements OrderState {
name(): string {
return 'shipped';
}
pay(_order: Order): void {
throw new Error('already paid');
}
ship(_order: Order): void {
throw new Error('already shipped');
}
}
const order = new Order();
order.pay();
order.ship();
console.log(order.status()); // "shipped"
  • ข้อดี: แทนที่เงื่อนไขต่อสถานะที่พันกันยุ่งด้วย object ที่เกาะกลุ่มกันหนึ่งตัวต่อสถานะ
  • ข้อดี: การเปลี่ยนผ่านเป็นไปอย่างชัดเจนและอยู่ข้างพฤติกรรมที่กระตุ้น ซึ่งทำให้ state machine อ่านง่าย
  • ข้อดี: การเพิ่มสถานะหมายถึงการเพิ่ม class โดยไม่แตะต้องสถานะที่มีอยู่
  • ข้อเสีย: มี class มากกว่าแฟล็กสถานะตัวเดียว ซึ่งเกินจำเป็นสำหรับสองหรือสามสถานะเล็กน้อย
  • ข้อเสีย: ตรรกะการเปลี่ยนผ่านกระจายอยู่ทั่วสถานะต่าง ๆ แผนที่เต็มของ machine จึงไม่ได้อยู่ที่เดียว
  • Strategy ใช้ไดอะแกรม class เดียวกัน แต่ intent แยกทางกัน strategy ถูกเลือกโดย client เพื่อทำให้อัลกอริทึมผันแปร ส่วน object สถานะเลือกตัวต่อจากตัวเองเมื่อเงื่อนไขของ object เปลี่ยน object Strategy มักไม่รับรู้ถึงกันและกัน object สถานะรู้จักสถานะที่เปลี่ยนผ่านไป
  • Singleton บางครั้งถูกใช้กับ object สถานะที่ไร้สถานะ เพื่อให้แชร์ใช้ได้แทนที่จะสร้างใหม่ทุกครั้งที่เปลี่ยนผ่าน
StateStrategyChain of Responsibility
ใครเปลี่ยน behaviorobject เองเมื่อ state เปลี่ยนclient inject จากนอกส่งต่อตาม chain จนมีคนรับ
รู้จักกันstate object รู้จัก transitionไม่รู้จักกันhandler รู้จัก next
จำนวน behaviorหนึ่งตัวที่ activeหนึ่งตัวที่เลือกไว้หลายตัว (ใครจัดการก็ได้)
เมื่อใช้FSM, order status, connectionalgorithm ที่สลับกันได้auth, validation, middleware
State pattern ยอมให้ object ทำสิ่งใด?
กฎการเปลี่ยนผ่านอยู่ที่ไหนใน State pattern?
State ต่างจาก Strategy ในด้าน intent อย่างไร?
อะไรเข้ามาแทนที่เงื่อนไขต่อสถานะที่พันกันยุ่ง?