Hướng dẫn State Pattern trong Java
1. Giới thiệu State Pattern
State Pattern là một mẫu thiết kế thuộc nhóm Behavioral Patterns, cho phép một đối tượng thay đổi hành vi của nó khi trạng thái nội tại của nó thay đổi. Mẫu thiết kế này giúp quản lý trạng thái và hành vi của đối tượng một cách rõ ràng và dễ bảo trì.
2. Các trường hợp ứng dụng State Pattern
Quản lý trạng thái phức tạp: Khi một đối tượng có nhiều trạng thái và hành vi của nó phụ thuộc vào các trạng thái này.
Chuyển đổi trạng thái động: Khi cần chuyển đổi trạng thái của đối tượng một cách linh hoạt tại thời điểm chạy.
Tách biệt logic trạng thái: Khi cần tách biệt logic xử lý của từng trạng thái để dễ bảo trì và mở rộng.
3. Triển khai State Pattern
3.1. Ví dụ triển khai đơn giản
Giả sử chúng ta có một đối tượng Context đại diện cho một máy bán hàng tự động với các trạng thái khác nhau như NoCoinState, HasCoinState, SoldState, và SoldOutState.
// Bước 1: Tạo interface State
public interface State {
void insertCoin();
void ejectCoin();
void dispense();
}
// Bước 2: Tạo các lớp trạng thái cụ thể
public class NoCoinState implements State {
private VendingMachine vendingMachine;
public NoCoinState(VendingMachine vendingMachine) {
this.vendingMachine = vendingMachine;
}
@Override
public void insertCoin() {
System.out.println("Coin inserted.");
vendingMachine.setState(vendingMachine.getHasCoinState());
}
@Override
public void ejectCoin() {
System.out.println("No coin to eject.");
}
@Override
public void dispense() {
System.out.println("Insert coin first.");
}
}
public class HasCoinState implements State {
private VendingMachine vendingMachine;
public HasCoinState(VendingMachine vendingMachine) {
this.vendingMachine = vendingMachine;
}
@Override
public void insertCoin() {
System.out.println("Coin already inserted.");
}
@Override
public void ejectCoin() {
System.out.println("Coin ejected.");
vendingMachine.setState(vendingMachine.getNoCoinState());
}
@Override
public void dispense() {
System.out.println("Dispensing product.");
vendingMachine.setState(vendingMachine.getSoldState());
}
}
public class SoldState implements State {
private VendingMachine vendingMachine;
public SoldState(VendingMachine vendingMachine) {
this.vendingMachine = vendingMachine;
}
@Override
public void insertCoin() {
System.out.println("Please wait, dispensing in progress.");
}
@Override
public void ejectCoin() {
System.out.println("Cannot eject coin after dispensing.");
}
@Override
public void dispense() {
System.out.println("Dispensing product.");
if (vendingMachine.getProductCount() > 0) {
vendingMachine.setState(vendingMachine.getNoCoinState());
} else {
System.out.println("Out of stock.");
vendingMachine.setState(vendingMachine.getSoldOutState());
}
}
}
public class SoldOutState implements State {
private VendingMachine vendingMachine;
public SoldOutState(VendingMachine vendingMachine) {
this.vendingMachine = vendingMachine;
}
@Override
public void insertCoin() {
System.out.println("Out of stock.");
}
@Override
public void ejectCoin() {
System.out.println("No coin to eject.");
}
@Override
public void dispense() {
System.out.println("Out of stock.");
}
}
// Bước 3: Tạo lớp Context
public class VendingMachine {
private State noCoinState;
private State hasCoinState;
private State soldState;
private State soldOutState;
private State currentState;
private int productCount;
public VendingMachine(int productCount) {
noCoinState = new NoCoinState(this);
hasCoinState = new HasCoinState(this);
soldState = new SoldState(this);
soldOutState = new SoldOutState(this);
this.productCount = productCount;
currentState = productCount > 0 ? noCoinState : soldOutState;
}
public void insertCoin() {
currentState.insertCoin();
}
public void ejectCoin() {
currentState.ejectCoin();
}
public void dispense() {
currentState.dispense();
}
public void setState(State state) {
currentState = state;
}
public State getNoCoinState() {
return noCoinState;
}
public State getHasCoinState() {
return hasCoinState;
}
public State getSoldState() {
return soldState;
}
public State getSoldOutState() {
return soldOutState;
}
public int getProductCount() {
return productCount;
}
public void releaseProduct() {
if (productCount > 0) {
productCount--;
}
}
}
// Bước 4: Sử dụng State Pattern
public class StatePatternDemo {
public static void main(String[] args) {
VendingMachine vendingMachine = new VendingMachine(5);
vendingMachine.insertCoin();
vendingMachine.dispense();
vendingMachine.insertCoin();
vendingMachine.ejectCoin();
vendingMachine.dispense();
}
}
3.2. Ví dụ triển khai phức tạp
Giả sử chúng ta có một hệ thống quản lý đơn hàng với các trạng thái khác nhau như NewOrderState, ProcessingState, ShippedState, và DeliveredState.
// Bước 1: Tạo interface State
public interface OrderState {
void next(OrderContext context);
void prev(OrderContext context);
void printStatus();
}
// Bước 2: Tạo các lớp trạng thái cụ thể
public class NewOrderState implements OrderState {
@Override
public void next(OrderContext context) {
context.setState(new ProcessingState());
}
@Override
public void prev(OrderContext context) {
System.out.println("The order is in its root state.");
}
@Override
public void printStatus() {
System.out.println("Order placed, not processed yet.");
}
}
public class ProcessingState implements OrderState {
@Override
public void next(OrderContext context) {
context.setState(new ShippedState());
}
@Override
public void prev(OrderContext context) {
context.setState(new NewOrderState());
}
@Override
public void printStatus() {
System.out.println("Order is being processed.");
}
}
public class ShippedState implements OrderState {
@Override
public void next(OrderContext context) {
context.setState(new DeliveredState());
}
@Override
public void prev(OrderContext context) {
context.setState(new ProcessingState());
}
@Override
public void printStatus() {
System.out.println("Order shipped to delivery address.");
}
}
public class DeliveredState implements OrderState {
@Override
public void next(OrderContext context) {
System.out.println("This order is already delivered.");
}
@Override
public void prev(OrderContext context) {
context.setState(new ShippedState());
}
@Override
public void printStatus() {
System.out.println("Order delivered to customer.");
}
}
// Bước 3: Tạo lớp Context
public class OrderContext {
private OrderState state;
public OrderContext() {
state = new NewOrderState();
}
public void setState(OrderState state) {
this.state = state;
}
public void next() {
state.next(this);
}
public void prev() {
state.prev(this);
}
public void printStatus() {
state.printStatus();
}
}
// Bước 4: Sử dụng State Pattern
public class StatePatternDemo {
public static void main(String[] args) {
OrderContext context = new OrderContext();
context.printStatus();
context.next();
context.printStatus();
context.next();
context.printStatus();
context.next();
context.printStatus();
context.prev();
context.printStatus();
}
}
4. Phân tích các trường hợp ứng dụng State Pattern
4.1. Quản lý trạng thái phức tạp
Ưu điểm: Giúp quản lý các trạng thái phức tạp của đối tượng một cách rõ ràng và dễ bảo trì.
Nhược điểm: Cần tạo nhiều lớp trạng thái, có thể làm tăng độ phức tạp của hệ thống.
4.2. Chuyển đổi trạng thái động
Ưu điểm: Cho phép chuyển đổi trạng thái của đối tượng một cách linh hoạt tại thời điểm chạy.
Nhược điểm: Cần đảm bảo rằng các trạng thái được quản lý và chuyển đổi một cách nhất quán.
4.3. Tách biệt logic trạng thái
Ưu điểm: Giúp tách biệt logic xử lý của từng trạng thái, dễ dàng bảo trì và mở rộng.
Nhược điểm: Cần thiết kế các lớp trạng thái một cách cẩn thận để tránh xung đột và lỗi xử lý.