Trước tiên, chúng ta hãy cùng cafedev giới thiệu mọi thứ về Command Pattern và phần code ví dụ chi tiết nhằm giúp ace dễ hiểu khi áp dụng trên các ngôn ngữ khác nhau. Ace có thể tham khảo thêm các bài khác tại series Design Pattern tại đây.

Giống như các  bài viết trước , chúng ta hãy cùng tìm hiểu một vấn đề thiết kế để hiểu về Command Pattern. Giả sử bạn đang xây dựng một hệ thống tự động hóa gia đình. Có một điều khiển từ xa có thể lập trình có thể được sử dụng để bật và tắt các vật dụng khác nhau trong nhà của bạn như đèn, âm thanh nổi, AC, v.v. Nó trông giống như thế này.

Bạn có thể làm điều đó với các câu lệnh if-else đơn giản như

if (buttonPressed == button1)
     lights.on()

Nhưng chúng ta cần lưu ý rằng việc bật một số thiết bị như âm thanh nổi bao gồm nhiều bước như cài đặt cd, âm lượng, v.v. Ngoài ra, chúng ta có thể gán lại một nút để làm việc khác. Bằng cách sử dụng if-else đơn giản, chúng ta đang viết code để triển khai thay vì giao diện. Ngoài ra có khớp nối chặt chẽ.

Vì vậy, những gì chúng ta muốn đạt được là một thiết kế cung cấp khớp nối lỏng lẻo và điều khiển từ xa không nên có nhiều thông tin về một thiết bị cụ thể. Command Pattern giúp chúng ta làm điều đó.

1. Định nghĩa:  

Command Pattern  đóng gói một yêu cầu dưới dạng một đối tượng, do đó cho phép chúng ta tham số hóa các đối tượng khác với các yêu cầu khác nhau, yêu cầu hàng đợi hoặc nhật ký và hỗ trợ các hoạt động hoàn tác vụ.

Định nghĩa này hơi khó hiểu lúc đầu nhưng chúng ta hãy bước qua nó. Tương tự với vấn đề của chúng ta ở trên điều khiển từ xa là client và âm thanh nổi, đèn, v.v. là máy thu. Trong Command Pattern, có một đối tượng Command đóng gói một yêu cầu bằng cách liên kết với nhau một tập hợp các hành động trên một bộ thu cụ thể. Nó làm như vậy bằng cách chỉ để lộ một phương thức execute() khiến một số hành động được gọi trên máy thu.

Việc tham số hóa các đối tượng khác với các yêu cầu khác nhau theo cách tương tự của chúng ta có nghĩa là nút dùng để bật đèn sau này có thể được sử dụng để bật âm thanh nổi hoặc có thể mở cửa nhà để xe.

yêu cầu hàng đợi hoặc nhật ký và hỗ trợ các hoạt động hoàn tác có nghĩa là hoạt động Thực thi của Lệnh có thể lưu trữ trạng thái để đảo ngược các tác động của nó trong chính Lệnh(Command). Lệnh(Command) có thể có thêm thao tác unExecute đảo ngược các tác động của lệnh gọi trước đó để thực thi. Nó cũng có thể hỗ trợ các thay đổi ghi nhật ký để chúng có thể được áp dụng lại trong trường hợp hệ thống gặp sự cố.

Dưới đây là cách triển khai Java của ví dụ điều khiển từ xa được đề cập ở trên: 

// A simple Java program to demonstrate 
// implementation of Command Pattern using 
// a remote control example. 

// An interface for command 
interface Command 
{ 
	public void execute(); 
} 

// Light class and its corresponding command 
// classes 
class Light 
{ 
	public void on() 
	{ 
		System.out.println("Light is on"); 
	} 
	public void off() 
	{ 
		System.out.println("Light is off"); 
	} 
} 
class LightOnCommand implements Command 
{ 
	Light light; 

	// The constructor is passed the light it 
	// is going to control. 
	public LightOnCommand(Light light) 
	{ 
	this.light = light; 
	} 
	public void execute() 
	{ 
	light.on(); 
	} 
} 
class LightOffCommand implements Command 
{ 
	Light light; 
	public LightOffCommand(Light light) 
	{ 
		this.light = light; 
	} 
	public void execute() 
	{ 
		light.off(); 
	} 
} 

// Stereo and its command classes 
class Stereo 
{ 
	public void on() 
	{ 
		System.out.println("Stereo is on"); 
	} 
	public void off() 
	{ 
		System.out.println("Stereo is off"); 
	} 
	public void setCD() 
	{ 
		System.out.println("Stereo is set " + 
						"for CD input"); 
	} 
	public void setDVD() 
	{ 
		System.out.println("Stereo is set"+ 
						" for DVD input"); 
	} 
	public void setRadio() 
	{ 
		System.out.println("Stereo is set" + 
						" for Radio"); 
	} 
	public void setVolume(int volume) 
	{ 
	// code to set the volume 
	System.out.println("Stereo volume set"
						+ " to " + volume); 
	} 
} 
class StereoOffCommand implements Command 
{ 
	Stereo stereo; 
	public StereoOffCommand(Stereo stereo) 
	{ 
		this.stereo = stereo; 
	} 
	public void execute() 
	{ 
	stereo.off(); 
	} 
} 
class StereoOnWithCDCommand implements Command 
{ 
	Stereo stereo; 
	public StereoOnWithCDCommand(Stereo stereo) 
	{ 
		this.stereo = stereo; 
	} 
	public void execute() 
	{ 
		stereo.on(); 
		stereo.setCD(); 
		stereo.setVolume(11); 
	} 
} 

// A Simple remote control with one button 
class SimpleRemoteControl 
{ 
	Command slot; // only one button 

	public SimpleRemoteControl() 
	{ 
	} 

	public void setCommand(Command command) 
	{ 
		// set the command the remote will 
		// execute 
		slot = command; 
	} 

	public void buttonWasPressed() 
	{ 
		slot.execute(); 
	} 
} 

// Driver class 
class RemoteControlTest 
{ 
	public static void main(String[] args) 
	{ 
		SimpleRemoteControl remote = 
				new SimpleRemoteControl(); 
		Light light = new Light(); 
		Stereo stereo = new Stereo(); 

		// we can change command dynamically 
		remote.setCommand(new
					LightOnCommand(light)); 
		remote.buttonWasPressed(); 
		remote.setCommand(new
				StereoOnWithCDCommand(stereo)); 
		remote.buttonWasPressed(); 
		remote.setCommand(new
				StereoOffCommand(stereo)); 
		remote.buttonWasPressed(); 
	} 
} 

Đầu ra:

Light is on
Stereo is on
Stereo is set for CD input
Stereo volume set to 11
Stereo is off

Lưu ý rằng điều khiển từ xa không biết gì về việc bật âm thanh nổi. Thông tin đó được chứa trong một đối tượng lệnh riêng biệt. Điều này làm giảm sự ghép nối giữa chúng.

2. Ưu điểm:

  • Làm cho code của chúng ta có thể mở rộng được vì chúng ta có thể thêm các lệnh mới mà không cần thay đổi mã hiện có.
  • Giảm sự kết hợp giữa người gọi và người nhận lệnh.

3. Nhược điểm:

  • Tăng số lớp cho từng lệnh riêng lẻ

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!