Loại máy trạng thái an toàn trong TypeScript


Huỳnh Tuyết Oanh
1 năm trước
Hữu ích 2 Chia sẻ Viết bình luận 0
Đã xem 4821

Máy nhà nước ở khắp mọi nơi nhưng tôi không nghĩ rằng họ có đủ thời gian không khí. Để khắc phục tình trạng tôi sẽ trình bày một triển khai trong TypeScript và sử dụng nó để mô hình một thang máy rất đơn giản.

Tôi có thể chỉ cho bạn định nghĩa về một máy trạng thái trên Wikipedia nhưng cái tên cho nó đi. Máy trạng thái bao gồm các trạng thái và mô tả về cách đi từ trạng thái này sang trạng thái tiếp theo. Vì vậy, hãy bắt đầu đánh vần mọi thứ.

Điều đầu tiên chúng ta cần là một mô tả về các tiểu bang. Tôi sẽ sử dụng các loại cơ bản như chuỗi, số và ký hiệu vì nó đơn giản hóa rất nhiều phần còn lại của việc triển khai và chúng tôi không mất bất kỳ tính tổng quát nào

type Primitives = string | number | symbol;

Tiếp theo chúng ta cần một mô tả về chức năng / bản đồ chuyển tiếp

type EventTypeMapping<I extends Primitives> = { [K in I]: unknown };

type TransitionMap<I extends Primitives, S, E extends EventTypeMapping<I>> = {
  [K in I]: (event: E[I], currentState: I, extraState: S) => I
};

Định nghĩa đó có vẻ hơi phức tạp nhưng trong tiếng Anh đơn giản, nó nói với mọi trạng thái chúng ta có một hàm lấy một sự kiện ( E[I]), trạng thái hiện tại ( I), một số trạng thái bổ sung ( S) và đưa chúng ta trở lại trạng thái tiếp theo. Các sự kiện được lập chỉ mục bởi các trạng thái có thể mà máy có thể tham gia vì không phải mọi sự kiện đều hợp lệ ở mọi trạng thái. Không có cách nào để thực sự thực thi ràng buộc này tại thời điểm biên dịch, vì vậy các hàm chuyển đổi của chúng ta phải xử lý tất cả các loại sự kiện là về cái gì E[I].

Bây giờ chúng ta có thể xây dựng máy. Chúng tôi sẽ sử dụng một lớp để mô hình hóa máy với các thành phần trên

export class Machine<I extends Primitives, S, E extends EventTypeMapping<I>> {

  constructor(
    readonly state: S,
    readonly initialState: I,
    readonly transitions: TransitionMap<I, S, E>,
    public currentState: I = initialState
  ) { }
}

Máy ở trên không làm được gì nhiều vì chúng tôi không tận dụng các chức năng chuyển đổi, vì vậy hãy khắc phục điều đó.

// ...
  step(event: E[I]): [I, I] {
    const currentState = this.currentState;
    const newState = this.transitions[currentState](
      event, currentState, this.state);
    this.currentState = newState;
    return [currentState, newState];
  }
// ...

Đó là nó. Chúng tôi chỉ thực hiện một máy trạng thái trừu tượng. Các máy trạng thái trừu tượng không hữu ích, vì vậy hãy triển khai một máy cụ thể mô hình thang máy trong tòa nhà 3 tầng.

Thang máy sẽ bắt đầu từ tầng 1 và sau đó đi hết tầng 3. Sau khi đến tầng 3, nó sẽ đi xuống tầng 1 và sau đó lặp lại chu trình này. Như tôi đã nói, nó rất đơn giản.

Điều đầu tiên chúng ta cần là mô tả về các trạng thái. Phần có liên quan nhất của tiểu bang là tầng thang máy đang bật nên đó là những gì chúng ta sẽ sử dụng cho mô tả trạng thái.

type Floor = 1 | 2 | 3;

Thang máy này không có bất kỳ đầu vào nào nên việc lập bản đồ sự kiện cho từng trạng thái cũng rất đơn giản.

type AllowedEvents = { 1: void; 2: void; 3: void };

Chúng tôi cần thêm một chút thông tin. Thang máy cần biết nó đi theo hướng nào.

type ExtraState = { direction: 'up' | 'down' };

Các chức năng chuyển tiếp là một mối quan tâm về thời gian chạy vì vậy để lấp đầy chúng trong chúng ta sẽ khởi tạo máy trừu tượng của chúng ta và điền chúng vào.

const elevator = new Machine<Floor, ExtraState, AllowedEvents>(
  { direction: 'up' },
  1,
  {
    1: (e, f, s) => { return (s.direction = 'up', 2); }, // Can only go up
    2: (e, f, s) => { return s.direction === 'up' ? 3 : 1 }, // Can go up or down
    3: (e, f, s) => { return (s.direction = 'down', 2); } // Can only go down
  }
);

Chúng ta cũng bước qua các lần chuyển đổi một vài lần để xác minh rằng nó hoạt động như chúng ta mong đợi.

console.log(`*starting*`);
for (let i = 0; i < 12; i++) {
  const [previousFloor, nextFloor] = elevator.step(void(0));
  console.log(`Elevator going ${elevator.state.direction}: ${previousFloor} -> ${nextFloor}`);
  if (elevator.currentState === 1) {
    console.log(`---Cycle complete---`);
  }
}
console.log(`*done*`);
*starting*
Elevator going up: 1 -> 2
Elevator going up: 2 -> 3
Elevator going down: 3 -> 2
Elevator going down: 2 -> 1
---Cycle complete---
Elevator going up: 1 -> 2
Elevator going up: 2 -> 3
Elevator going down: 3 -> 2
Elevator going down: 2 -> 1
---Cycle complete---
Elevator going up: 1 -> 2
Elevator going up: 2 -> 3
Elevator going down: 3 -> 2
Elevator going down: 2 -> 1
---Cycle complete---
*done*

Có vẻ đúng với tôi. Làm cho thang máy bớt đơn giản là một bài tập cho người đọc. Xem nếu bạn có thể mô hình hóa những người yêu cầu được đưa đến một tầng cụ thể làm điểm bắt đầu.

Tất cả các mã cùng với ví dụ trên có tại GitHub: davidk01 / state-machine .

Hữu ích 2 Chia sẻ Viết bình luận 0
Đã xem 4821