Mẫu trạng thái
Thuộc phần Lập trình hướng đối tượng trong hành trình C# của Coddy. Bài học 60 trên 70.
Mẫu State là một mẫu hành vi cho phép đối tượng thay đổi hành vi khi trạng thái bên trong thay đổi. Thay vì dùng các câu lệnh điều kiện phức tạp để xử lý những trạng thái khác nhau, bạn đóng gói mỗi trạng thái trong lớp riêng và ủy quyền hành vi cho đối tượng trạng thái hiện tại.
Mẫu này gồm ba thành phần: một giao diện trạng thái xác định hành vi riêng của từng trạng thái, các lớp trạng thái cụ thể triển khai hành vi đó và một lớp ngữ cảnh duy trì tham chiếu đến trạng thái hiện tại:
public interface IDocumentState
{
void Publish(Document doc);
}
public class DraftState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Moving to moderation");
doc.SetState(new ModerationState());
}
}
public class ModerationState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Publishing document");
doc.SetState(new PublishedState());
}
}
public class PublishedState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Already published");
}
}Đối tượng ngữ cảnh lưu trạng thái hiện tại và ủy quyền các thao tác cho trạng thái đó. Các đối tượng trạng thái có thể kích hoạt chuyển đổi bằng cách gọi SetState() trên đối tượng ngữ cảnh:
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
var doc = new Document();
doc.Publish(); // Chuyển sang trạng thái chờ duyệt
doc.Publish(); // Xuất bản tài liệu
doc.Publish(); // Đã xuất bảnMẫu trạng thái (State pattern) loại bỏ các câu lệnh if-else hoặc switch dài dòng dùng để kiểm tra trạng thái hiện tại. Mỗi lớp trạng thái chỉ xử lý hành vi riêng của mình, giúp bạn dễ bảo trì code và mở rộng bằng các trạng thái mới.
Thử thách
DễHãy xây dựng hệ thống đèn giao thông bằng mẫu State. Đèn giao thông luân phiên qua các trạng thái riêng biệt (đỏ, vàng, xanh lá), và mỗi trạng thái quyết định điều gì xảy ra khi đèn chuyển trạng thái. Thay vì dùng các câu lệnh điều kiện phức tạp, bạn sẽ đóng gói từng trạng thái đèn trong lớp riêng.
Bạn sẽ sắp xếp code trong ba tệp:
TrafficLightState.cs: Định nghĩa giao diệnITrafficLightStatetrong không gian tênTrafficvới phương thứcChange(TrafficLight light). Sau đó tạo ba lớp trạng thái cụ thể:RedState, khi gọiChange, lớp này in raRed - Stop! Changing to Green...và chuyển đèn sangGreenStateGreenState, in raGreen - Go! Changing to Yellow...và chuyển sangYellowStateYellowState, in raYellow - Caution! Changing to Red...và chuyển sangRedState
TrafficLight.cs: Tạo lớpTrafficLighttrong cùng không gian tên. Đây là lớp ngữ cảnh duy trì trạng thái hiện tại. Theo mặc định, lớp bắt đầu ở trạng tháiRedStatevà có:- Phương thức
SetState(ITrafficLightState state)để các đối tượng trạng thái dùng khi chuyển trạng thái - Phương thức
Change()ủy quyền cho phương thứcChangecủa trạng thái hiện tại
- Phương thức
Program.cs: Kết hợp mọi thứ bằng cách tạo mộtTrafficLightvà luân phiên chuyển trạng thái dựa trên đầu vào.
Bạn sẽ nhận một đầu vào:
- Số lần cần chuyển trạng thái của đèn giao thông (một số nguyên)
Tạo một TrafficLight (ban đầu ở trạng thái đỏ) và gọi Change() số lần đã cho.
Ví dụ, nếu đầu vào là 3, đầu ra sẽ là:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...Nếu đầu vào là 5, đèn sẽ quay lại trạng thái ban đầu:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...Lưu ý rằng lớp TrafficLight không chứa logic về ý nghĩa của từng màu hoặc trạng thái tiếp theo. Tất cả kiến thức đó nằm trong các lớp trạng thái riêng. Mỗi trạng thái chịu trách nhiệm cho hành vi của mình và kích hoạt quá trình chuyển sang trạng thái tiếp theo!
Tự làm thử
using System;
using Traffic;
class Program
{
public static void Main(String[] args)
{
// Đọc số lần chuyển trạng thái đèn giao thông
int n = Convert.ToInt32(Console.ReadLine());
// TODO: Tạo một instance TrafficLight (mặc định bắt đầu ở RedState)
// TODO: Dùng vòng lặp để gọi phương thức (method) Change() n lần
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Các khái niệm cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiKhông gian tên và chỉ thịGiới thiệu về lớp và đối tượngTừ khóa thisPhương thức và tham sốTrường và thuộc tínhHàm khởi tạoKhởi tạo đối tượngÔn tập: Máy tính đơn giản4Kế thừa
Cú pháp kế thừa cơ bản (:)Từ khóa 'base'Từ khóa virtual và overrideLớp sealedLớp cơ sở 'object'Ôn tập: hệ phân cấp nhân viên7Tính năng nâng cao
Nạp chồng toán tửBộ lập chỉ mục (this[])Ghi đè ToString()Phương thức mở rộngÔn tập: Danh sách tùy chỉnh10Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếSingleton an toàn với luồngMẫu FactoryMẫu Observer (sự kiện)Mẫu Strategy2Thuộc tính và thành viên tĩnh
Thuộc tính tự độngThuộc tính chỉ đọc hoặc chỉ ghiTrường và phương thức tĩnhLớp tĩnhThành viên có thân biểu thức5Đa hình và interface
Đa hình lúc biên dịch và lúc chạyInterface và lớp trừu tượngNhiều interfaceInterface tường minhUpcasting và downcastingÔn tập: bộ tính toán hình học8Các khái niệm OOP nâng cao
Ưu tiên composition hơn kế thừaGenerics (lớp và phương thức)Delegate và sự kiệnAttribute và reflectionGiao diện IDisposable và câu lệnh usingKiến thức cơ bản về dependency injection11Các mẫu thiết kế phần 2
Mẫu lệnhMẫu bộ chuyển đổiMẫu trang tríMẫu phương thức khuôn mẫuMẫu trạng tháiMẫu tổ hợpTự luyện tập: Trình biên dịch C# online