State Design Pattern
Bài đăng này đã không được cập nhật trong 5 năm
State Pattern là gì?
State Pattern
là một trong những mẫu thiết kế thuộc nhóm behavioral cho phép một object có thể biến đổi hành vi của nó khi có những sự thay đổi trạng thái nội bộ.
Mẫu thiết kế này có thể được hiểu gần giống như Strategy
, nó có thể chuyển đổi các chiến lược thông qua các phương thức được định nghĩa trong interface
Bài toán
Hãy xem xét một lớp TCPConnection
đại diện cho một kết nối mạng. Đối tượng kết nối TCPConnection
có thể ở một trong một số trạng thái khác nhau: Established
, Listening
, Closed
. Khi một đối tượng TCPConnection
nhận được yêu cầu từ các đối tượng khác, nó sẽ thực hiện hành vi khác nhau tùy thuộc vào trạng thái hiện tại của nó.
Ví dụ: Kết quả đạt được của một Open request phụ thuộc vào việc kết nối ở trạng thái Closed
hay Established
. State pattern mô tả cách TCPConnection
thể hiện hành vi khác nhau tùy vào mỗi trạng thái
Ý tưởng chính trong mẫu này là giới thiệu một lớp trừu tượng gọi là TCPState
để đại diện cho các trạng thái của kết nối mạng. Lớp TCPState
khai báo một interface chung cho tất cả các lớp thể hiện các trạng thái hoạt động khác nhau. Các lớp con của TCPState
sẽ được cài đặt những hành vi cụ thể.
Ví dụ: lớp TCPEstablished
và TCPClosed
sẽ triển khai hành động cụ thể cho trạng thái Established và Closed của TCPConnection
Ứng dụng của State Pattern
Chúng ta sẽ áp dụng State Pattern trong các trường hợp như sau:
- Hành vi của một đội tượng phụ thuộc vào trạng thái của nó. Tại thời điểm runtime, khi đối tượng thực hiện hành vi, trạng thái của nó sẽ thay đổi theo.
- Đối tượng có nhiều trường hợp sử dụng với các hành vi của nó, nhiều hành vi phụ thuộc vào trạng thái của đối tượng. Hay nói cách khác, đối tượng có nhiều trạng thái, mỗi trạng thái có những hành vi khác nhau.
Cấu trúc của State Pattern
Thành phần chính
Context
- Định nghĩa giao diện chính để giao tiếp với clients.
- Chứa một thể hiện của
ConcreateState
tương ứng với trạng thái hiện tại của đối tượng.
State
- Định nghĩa giao diện để đóng gói những hành vi giao tiếp với từng trạng thái của
Context
ConcreateState subclasses
- Mỗi một class con implements một hành vi giao tiếp với một trạng thái của
Context
Luồng hoạt động
Context
sẽ định nghĩa những hành vi có thể giao tiếp với Clients, vì thế clients sẽ yêu cầu các hành vi thông qua ContextContext
sẽ chứa một thể hiện củaState
,State
ban đầu có thể được cài đặt từ Client, nhưng khi đã cài đặt rồi thì clients không được sửa đổi nó nữa.Context
có thể gửi chính nó như một arguments tớiState
, vì thếState
có thể truy cập vàoContext
để thay đổi trạng thái nếu cần thiết.- Khi
Context
thực hiện hành vi, nó sẽ gọiState
hiện tại để thực hiện hành vi đó, thực hiện xong,State
có thể sẽ thay đổi trạng thái từ Context nếu cần
Kết quả
Phân vùng hành vi của đối tượng với những trạng thái khác nhau.
Đối tượng được thay đổi trạng thái một cách rõ ràng.
Trạng thái của những đối tượng có thể chia sẻ lẫn nhau.
Ví dụ mẫu
Chúng ta sẽ định nghĩa một interface State
và có 2 trạng thái riêng của nó là LowerCaseState
và MultipleUpperCaseState
interface State {
void writeName(StateContext context, String name);
}
class LowerCaseState implements State {
@Override
public void writeName(StateContext context, String name) {
System.out.println(name.toLowerCase());
context.setState(new MultipleUpperCaseState());
}
}
class MultipleUpperCaseState implements State {
/* Counter local to this state */
private int count = 0;
@Override
public void writeName(StateContext context, String name) {
System.out.println(name.toUpperCase());
/* Change state after StateMultipleUpperCase's writeName() gets invoked twice */
if(++count > 1) {
context.setState(new LowerCaseState());
}
}
}
Lớp Context
sẽ chứa một biến state, đặc trưng là trạng thái hiện tại của Context
, trong hàm khởi tạo, state
sẽ được gán trạng thái mặc định.
Bên cạnh đó, lớp Context
sẽ định nghĩa hàm setter cho state
để thay đổi trạng thái mỗi khi thực hiện hành vi
Hành vi được thực hiện ở đây là writeName
class StateContext {
private State state;
public StateContext() {
state = new LowerCaseState();
}
/**
* Set the current state.
* Normally only called by classes implementing the State interface.
* @param newState the new state of this context
*/
void setState(State newState) {
state = newState;
}
public void writeName(String name) {
state.writeName(this, name);
}
}
Khi đó, tại hàm main (client), chúng ta sẽ khởi tạo Context và thực hiện hành vi của chúng:
public class StateDemo {
public static void main(String[] args) {
StateContext context = new StateContext();
context.writeName("Monday");
context.writeName("Tuesday");
context.writeName("Wednesday");
context.writeName("Thursday");
context.writeName("Friday");
context.writeName("Saturday");
context.writeName("Sunday");
}
}
Kết quả sẽ được in ra như sau:
monday
TUESDAY
WEDNESDAY
thursday
FRIDAY
SATURDAY
sunday
Các bạn có thể thấy, mỗi khi thực hiện hàm writeName, state lại được chuyển đổi một lần dẫn tới output của chúng ta cũng khác nhau trong mỗi lần thực hiện
Design Pattern liên quan
Nếu ở bên phần trên, một trong những kết quả của design pattern này là các trạng thái của objects có thể chia sẻ lẫn nhau, mẫu thiết kế Flyweight sẽ chỉ rõ khi nào cần chia sẻ những trạng thái và thực hiện như nào để chia sẻ được
Thông thường, những đối tượng State được thiết kế theo mẫu Singleton, vì bản thân các state chúng ta chỉ cần một thể hiện duy nhất
Tham khảo
[1] Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0-201-63361-2.
[2] http://www.w3sdesign.com/GoF_Design_Patterns_Reference0100.pdf
All rights reserved