author-pic

Ferry S

An ISTJ, Type 5, Engineer, Gamer, and Thriller-Movies-Lover
Contoh Command Design Pattern
Mon. Aug 30th, 2021 09:01 AM3 mins read
Contoh Command Design Pattern
Source: HuggingFace@TonyAssi - a man giving command to a dog

Yang udah pernah menggunakan Clean Architecture mungkin udah familiar dengan design pattern ini, terutama ketika declare input boundary. Selain itu, ketika kita melakukan pembungkusan logic ke dalam runnable juga termasuk Command Pattern. Design pattern yang satu ini memang cukup populer di kalangan software engineer. Termasuk salah satu design pattern yang gampang dipahami.

Command Design Pattern adalah Behavioral Design Pattern yang membungkus request menjadi objek tunggal yang menyediakan informasi tentang request tersebut untuk ditransformasikan menjadi method arguments, eksekusi request, antrian proses, dan undoable operations.

Design Pattern

Kita akan membuat system delete operation, yang mencakup delete user dan delete order.

  • Pertama akan dilakukan pengecekan apakah ada order yang dilakukan oleh user id dari request;
  • Jika ada maka akan dilakukan penghapusan order berdasarakan user id tersebut;
  • Lalu, akan dilakukan pengecekan apakah user id tersebut ada di user repository;
  • Jika ada maka akan dilakukan penghapusan user berdasarakan user id tersebut;

UserRepository Interface

public interface UserRepository{
	void deleteUserById(int id);
	boolean check(int userId);
}

OrderRepository Interface

public interface OrderRepository{
	void deleteOrderByUserId(int id);
	boolean check(int userId);
}

UserRepositoryImpl Class

public class UserRepositoryImpl implements UserRepository{
	@Override
	public void deleteUserById(int id){
		System.out.println("success deleted by user id: " + id);
	}

	@Override
	public boolean check(int userId){
		return true;
	}
}

OrderRepositoryImpl Class

public class OrderRepositoryImpl implements OrderRepository{
	@Override
	public void deleteOrderByUserId(int id){
		System.out.println("success deleted order by user id: " + id);
	}

	@Override
	public boolean check(int userId){
		return false;
	}
}

Contoh penggunaan

public static void main(String[] args){
	OrderRepository orderRepository = new OrderRepositoryImpl();
	UserRepository userRepository = new UserRepositoryImpl();
	int userId = 1;
	//delete order
	if(orderRepository.check(userId)){
		orderRepository.deleteOrderByUserId(userId);
	}

	//delete user
	if(userRepository.check(userId)){
		userRepository.deleteUserById(userId);
	}
}

Code di atas cukup simple dan cukup menjelaskan requirement yang dibutuhkan.

Terdapat dua buah action yang identik, yaitu delete user dan delete order. Disini client lah yang melakukan configurasi request. Misalkan ada penambahan action lagi seperti delete bank account by user id, maka user harus melakukan penambahan configurasi lagi di client code.

Sekarang kita coba membuat Command Design Pattern😎.

DeleteCommand Interface

public interface DeleteCommand{
	void execute(int userId);
}

UserDeleteCommand Class

public class UserDeleteCommand implements DeleteCommand{
	private final UserRepository repository;

	public UserDeleteCommand(UserRepository repository){
		this.repository = repository;
	}

	@Override
	public void execute(int userId){
		if(repository.check(userId)){
			repository.deleteUserById(userId);
		}
	}
}

OrderDeleteCommand Class

public class OrderDeleteCommand implements DeleteCommand{
	private final OrderRepository repository;

	public OrderDeleteCommand(OrderRepository repository){
		this.repository = repository;
	}

	@Override
	public void execute(int userId){
		if(repository.check(userId)){
			repository.deleteOrderByUserId(userId);
		}
	}
}

Contoh penggunaan

public static void main(String[] args){
	int userId = 1;
	//delete order
	invokeDelete(new OrderDeleteCommand(new OrderRepositoryImpl()), userId);

	//delete user
	invokeDelete(new UserDeleteCommand(new UserRepositoryImpl()), userId);
}

private static void invokeDelete(DeleteCommand deleteCommand, int userId){
	deleteCommand.execute(userId);
}

Kita hanya menambahkan Command Interface dan implementasinya. Kali ini kita membungkus operasi delete user dan delete order ke dalam sebuah objek. Untuk menggunakannya kita cukup membuat objek dari class tersebut dan melakukan eksekusi. Pada contoh di atas, method invokeDelete() berperan sebagai invoker dari command yang diinput. Ketika ada penambahan delete action baru, tinggal bikin class baru yang mengimplementasi DeleteCommand.

Command Design Pattern digunakan ketika ingin menjadikan sebuah action sebagai parameter. Oleh karena itu kita membungkusnya menjadi sebuah objek. Dengan begitu kita bisa mengirim objek yang berisi action yang identik dan fleksibel. Selain itu, command pattern juga bisa dimanfaatkan untuk antrian eksekusi, karena trigger proses eksekusinya bisa dilakukan belakangan seperti pada Runnable di Java.

Contoh Command Pattern yang paling sering dilakukan adalah ketika melakukan multi-threading menggunakan Runnable, di sana kita membungkus sebuah action ke dalam objek runnable sebelum dieksekusi nanti secara concurrent. Ini juga diimplementasi pada Clean Architecture untuk membungkus logic pada class use case. Sekilas memang terlihat seperti Strategy Design Pattern, tapi sebenarnya beda tujuan. Yang membedakan adalah, Command Design Pattern lebih spesifik tentang membungkus algoritma ke dalam sebuah objek yang memiliki sebuah method berupa perintah untuk mentrigger eksekusi algoritma tersebut. Sedangkan Strategy Design Pattern lebih general tentang penggunaan varian dari beberapa algoritma agar fleksibel dan maintainable. Dengan Command Pattern kita telah menerapkan Single Responsibility dan Open/Close Principle.