Kali ini gw membahas tentang Façade Design Pattern. Sebenarnya penggunaan design ini cukup umum sih. Gw rasa beberapa diantara kita udah familiar dengan Facade ketika membuat sebuah API. Bagi yang menerapkan Clean Architecture gw rasa udah sering menggunakan design pattern ini ketika membuat gateway. Tingkat kompleksitasnya juga cukup rendah, cukup mudah diimplementasikan.
Façade Design Pattern adalah Structural Design Pattern yang menyederhanakan sistem, library, atau framework yang kompleks ke dalam sebuah interface yang mudah digunakan.
Design Pattern
Use Case
Kita akan membuat sebuah system untuk melakukan pembuatan user dan penghapusan user dari database berdasarkan request yang diberikan.
- Ketika menambahkan user baru, kita dapat menambahkannya berdasarkan request yang diberikan;
- Setelah sukses kita akan mendapatkan id dari user yang disimpan tadi;
- Lalu dari user id tersebut, kita juga akan menambahkan user address berdasarkan address dari request dan user id tadi;
- Ketika menghapus user, kita perlu mengecek apakah user id tersebut digunakan oleh user dan user address;
- Jika ada, maka user dan user address akan dihapus berdasarkan user id yang dikirimkan dari request;
Contoh Code
UserRequest Class
public class UserRequest{
private final String userName;
private final String address;
public UserRequest(String userName, String address){
this.userName = userName;
this.address = address;
}
public String getUserName(){
return userName;
}
public String getAddress(){
return address;
}
}
UserRepository Interface
public interface UserRepository{
int createUser(UserEntity userEntity);
UserEntity getUserById(int id);
void deleteUser(int id);
}
UserRepositoryImpl Class
public class UserRepositoryImpl implements UserRepository{
@Override
public int createUser(UserEntity userEntity){
System.out.println("success create user");
return 1;
}
@Override
public UserEntity getUserById(int id){
System.out.println("getting user by id");
return new UserEntity();
}
@Override
public void deleteUser(int id){
System.out.println("success delete user by id");
}
}
UserAddressRepository Interface
public interface UserAddressRepository{
int createUserAddress(UserAddressEntity userAddressEntity);
UserAddressEntity getUserAddressById(int id);
void deleteUserAddress(int id);
}
UserAddressRepositoryImpl Class
public class UserAddressRepositoryImpl implements UserAddressRepository{
@Override
public int createUserAddress(UserAddressEntity userAddressEntity){
System.out.println("success create user address");
return 1;
}
@Override
public UserAddressEntity getUserAddressById(int id){
System.out.println("getting user address by id");
return new UserAddressEntity();
}
@Override
public void deleteUserAddress(int id){
System.out.println("success delete user address by id");
}
}
UserEntity Class
public class UserEntity{
private int userId;
private String userName;
public int getUserId(){
return userId;
}
public void setUserId(int userId){
this.userId = userId;
}
public String getUserName(){
return userName;
}
public void setUserName(String userName){
this.userName = userName;
}
}
UserAddressEntity Class
public class UserAddressEntity{
private int userId;
private String address;
public int getUserId(){
return userId;
}
public void setUserId(int userId){
this.userId = userId;
}
public String getAddress(){
return address;
}
public void setAddress(String address){
this.address = address;
}
}
Contoh Penggunaan
public static void main(String[] args){
UserRepositoryImpl userRepository = new UserRepositoryImpl();
UserAddressRepositoryImpl userAddressRepository = new UserAddressRepositoryImpl();
//create user
UserRequest userRequest = new UserRequest("ferry", "solok");
UserEntity userEntity = new UserEntity();
userEntity.setUserName(userRequest.getUserName());
int user = userRepository.createUser(userEntity);
UserAddressEntity userAddressEntity = new UserAddressEntity();
userAddressEntity.setUserId(user);
userAddressEntity.setAddress(userRequest.getAddress());
userAddressRepository.createUserAddress(userAddressEntity);
//delete user
int userId = 1;
UserAddressEntity userAddressById = userAddressRepository.getUserAddressById(userId);
if(userAddressById != null){
userAddressRepository.deleteUserAddress(userId);
}
UserEntity userById = userRepository.getUserById(userId);
if(userById != null){
userRepository.deleteUser(userId);
}
}
Kita sudah mengimplementasi requirement di atas. Implementasinya ditulis di dalam client code.
Masalah
Disini client sendiri yang melakukan eksekusi perintah sesuai requirement di atas. Itu semua terlalu complex dilakukan di dalam client code. Sehingga ketika terjadi perubahan, atau penambahan logic, semua client code harus di-refactor.
Solusi
Selanjutnya kita coba buat menggunakan Façade Design Pattern😎.
UserService Interface
public interface UserService{
void createUser(UserRequest userRequest);
void deleteUser(int id);
void createAddress(UserRequest userRequest, int userId);
void deleteAddress(int id);
}
UserServiceImpl Class
public class UserServiceImpl implements UserService{
private final UserRepository userRepository;
private final UserAddressRepository userAddressRepository;
public UserServiceImpl(UserRepository userRepository, UserAddressRepository userAddressRepository){
this.userRepository = userRepository;
this.userAddressRepository = userAddressRepository;
}
@Override
public void createUser(UserRequest userRequest){
UserEntity userEntity = new UserEntity();
userEntity.setUserName(userRequest.getUserName());
int user = userRepository.createUser(userEntity);
createAddress(userRequest, user);
}
@Override
public void createAddress(UserRequest userRequest, int userId){
UserAddressEntity userAddressEntity = new UserAddressEntity();
userAddressEntity.setUserId(userId);
userAddressEntity.setAddress(userRequest.getAddress());
userAddressRepository.createUserAddress(userAddressEntity);
}
@Override
public void deleteUser(int id){
deleteAddress(id);
UserEntity userById = userRepository.getUserById(id);
if(userById != null){
userRepository.deleteUser(id);
}
}
@Override
public void deleteAddress(int id){
UserAddressEntity userAddressById = userAddressRepository.getUserAddressById(id);
if(userAddressById != null){
userAddressRepository.deleteUserAddress(id);
}
}
}
Contoh Penggunaan
public static void main(String[] args){
//create user
UserRequest userRequest = new UserRequest("ferry", "solok");
UserService userService = new UserServiceImpl(new UserRepositoryImpl(), new UserAddressRepositoryImpl());
userService.createUser(userRequest);
//delete user
int userId = 1;
userService.deleteUser(userId);
}
Selain penambahan dua class di atas, kita tidak perlu melakukan perubahan di class lainnya. Sekarang client code hanya tinggal membuat object UserService dan execute method createUser() dan deleteUser(). Kalau mau membuat atau menghapus addressnya saja juga bisa. Tidak perlu repot-repot bikin lagi atau tau kompleksitas algoritmanya.
Kenapa menggunakan Façade Design Pattern?
Façade Design Pattern cocok digunakan untuk mengurangi kompleksitas subsystem menjadi lebih straightforward. Sangat wajar subsystem akan terus menjadi kompleks, oleh karena itu kita butuh interface yang menyederhanakan itu. Tapi jika terlalu banyak hal yang di-handle di dalamnya, Façade juga bisa menjadi God Object. Untuk itu maka perlu dipecah kembali dengan Façade baru yang lebih sederhana dan memiliki kohesi yang tinggi dari Facade sebelumnya.
Verdict
Penerapannya cukup simple dan gampang dipahami. Intinya tujuan Façade Design Pattern ini adalah untuk menyederhanakan sebuah logic yang complex ke dalam sebuah class yang lebih straightforward. Sehingga client yang menggunakan ga perlu tahu detail kompleksitas logic di dalamnya.