Memento secara bahasa artinya kenang-kenangan atau tandamata. Sesuai namanya, Memento itu tugasnya menyimpan “kenangan” dari state suatu objek. Jadi misalkan kita sudah sampai ke state tertentu, kita bisa kembali lagi ke “kenangan” yang pernah kita simpan. Contoh analoginya adalah pada database. Misalkan kita melakukan insertion A, B, C, dan D. Lalu kita menyimpan savepoint empat insertion tersebut. Lanjut kita melakukan insertion E, F, G, lalu terjadi error. Di sini kita bisa melakukan rollback ke tempat savepoint sebelumnya. Sehingga insertion yang hilang hanyalah insertion E, F, dan G. Insertion A, B, C, dan D masih tersimpan dan bisa di-commit. Kita tidak perlu kehilangan keseluruhan insertion gara-gara hal tersebut.
Memento Design Pattern adalah Behavioral Design Pattern yang membuat kita dapat menambahkan savepoint pada state suatu objek, dan bisa restore ke state sebelumnya saat savepoint tersebut dibuat tanpa mengeksploitasi rincian implementasi state dari objek tersebut.
Design Pattern
Use Case
Kita akan menggunakan class EmployeeProfile untuk menyimpan data karyawan, seperti info dasar yaitu NIK, dan nama karyawan, rincian alamat karyawan, serta rincian pendidikan. Misalkan terjadi error saat mengisi rincian alamat karyawan, maka seluruh data alamat karyawan akan di-rollback. Saat terjadi error ketika mengisi rincian pendidikan karyawan, maka seluruh data pendidikan karyawan akan di-rollback.
Contoh code
Class EmployeeProfile
public class EmployeeProfile{
private String name;
private int nik;
private String address;
private String province;
private String city;
private String elementarySchool;
private String juniorHighSchool;
private String seniorHighSchool;
public void setName(String name){
this.name = name;
}
public void setNik(int nik){
this.nik = nik;
}
public void setAddress(String address){
this.address = address;
}
public void setProvince(String province){
this.province = province;
}
public void setCity(String city){
this.city = city;
}
public void setElementarySchool(String elementarySchool){
this.elementarySchool = elementarySchool;
}
public void setJuniorHighSchool(String juniorHighSchool){
this.juniorHighSchool = juniorHighSchool;
}
public void setSeniorHighSchool(String seniorHighSchool){
this.seniorHighSchool = seniorHighSchool;
}
@Override
public String toString(){
return "EmployeeProfile{" +
"name='" + name + '\'' +
", nik=" + nik +
", address='" + address + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
", elementarySchool='" + elementarySchool + '\'' +
", juniorHighSchool='" + juniorHighSchool + '\'' +
", seniorHighSchool='" + seniorHighSchool + '\'' +
'}';
}
}
Contoh penggunaan
public static void main(String[] args){
EmployeeProfile profile = new EmployeeProfile();
profile.setNik(123321);
profile.setName("reza rahadian");
try{
profile.setAddress("jl. lintas sumatera");
profile.setCity("solok");
String province = getProvince();//imagine if this throw an exception
profile.setProvince(province);
} catch(Exception e){
profile.setAddress(null);
profile.setCity(null);
profile.setProvince(null);
}
try{
profile.setElementarySchool("sd 20");
profile.setJuniorHighSchool("smp 1");
String highSchool = getHighSchool();//imagine if this throw an exception
profile.setSeniorHighSchool(highSchool);
} catch(Exception e){
profile.setElementarySchool(null);
profile.setJuniorHighSchool(null);
profile.setSeniorHighSchool(null);
}
System.out.println("profile = " + profile);
}
private static String getHighSchool(){
if(true){
throw new RuntimeException();
}
return null;
}
private static String getProvince(){
return null;
}
Code di atas sudah sesuai kriteria yang dibutuhkan.
Masalah
Maintain dari code tersebut akan ribet karena logic dibuat di client code. Setiap ada perubahan kita harus mengganti client code yang menggunakan EmployeeProfile. Untuk itu kita butuh Memento Design Pattern agar code-nya jadi lebih bersih.
Solusi
Selanjutnya kita akan implementasi Memento Design Pattern😎. Memento Design Pattern biasanya ditandai dengan adanya sebuah class yang berfungsi menyimpan perubahan state yang dilakukan. Lalu ada objek yang bertugas mengatur savepoint dan rollback yang biasa disebut sebagai Memento Caretaker.
Class EmployeeProfile
public class EmployeeProfile{
private final Map<String, Memento> CARETAKER = new ConcurrentHashMap<>();
private String name;
private int nik;
private String address;
private String province;
private String city;
private String elementarySchool;
private String juniorHighSchool;
private String seniorHighSchool;
public void setName(String name){
this.name = name;
}
public void setNik(int nik){
this.nik = nik;
}
public void setAddress(String address){
this.address = address;
}
public void setProvince(String province){
this.province = province;
}
public void setCity(String city){
this.city = city;
}
public void setElementarySchool(String elementarySchool){
this.elementarySchool = elementarySchool;
}
public void setJuniorHighSchool(String juniorHighSchool){
this.juniorHighSchool = juniorHighSchool;
}
public void setSeniorHighSchool(String seniorHighSchool){
this.seniorHighSchool = seniorHighSchool;
}
public void savepoint(String savepointName){
Memento memento = new Memento(this.name, this.nik, this.address, this.province, this.city,
this.elementarySchool, this.juniorHighSchool, this.seniorHighSchool);
CARETAKER.put(savepointName, memento);
}
public void restore(String savepointName){
Memento memento = CARETAKER.get(savepointName);
if(memento == null){
throw new IllegalArgumentException("savepoint " + savepointName + " is not found");
}
this.name = memento.name;
this.nik = memento.nik;
this.address = memento.address;
this.province = memento.province;
this.city = memento.city;
this.elementarySchool = memento.elementarySchool;
this.juniorHighSchool = memento.juniorHighSchool;
this.seniorHighSchool = memento.seniorHighSchool;
}
@Override
public String toString(){
return "EmployeeProfile{" +
"name='" + name + '\'' +
", nik=" + nik +
", address='" + address + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
", elementarySchool='" + elementarySchool + '\'' +
", juniorHighSchool='" + juniorHighSchool + '\'' +
", seniorHighSchool='" + seniorHighSchool + '\'' +
'}';
}
private static class Memento{
private final String name;
private final int nik;
private final String address;
private final String province;
private final String city;
private final String elementarySchool;
private final String juniorHighSchool;
private final String seniorHighSchool;
Memento(String name, int nik, String address, String province, String city, String elementarySchool, String juniorHighSchool, String seniorHighSchool){
this.name = name;
this.nik = nik;
this.address = address;
this.province = province;
this.city = city;
this.elementarySchool = elementarySchool;
this.juniorHighSchool = juniorHighSchool;
this.seniorHighSchool = seniorHighSchool;
}
}
}
Contoh penggunaan
public static void main(String[] args){
EmployeeProfile profile = new EmployeeProfile();
profile.setNik(123321);
profile.setName("reza rahadian");
try{
profile.savepoint("basic");
profile.setAddress("jl. lintas sumatera");
profile.setCity("solok");
String province = getProvince();//imagine if this throw an exception
profile.setProvince(province);
} catch(Exception e){
profile.restore("basic");
}
try{
profile.savepoint("secondPoint");
profile.setElementarySchool("sd 20");
profile.setJuniorHighSchool("smp 1");
String highSchool = getHighSchool();//imagine if this throw an exception
profile.setSeniorHighSchool(highSchool);
} catch(Exception e){
profile.restore("secondPoint");
}
System.out.println("profile = " + profile);
}
private static String getHighSchool(){
return null;
}
private static String getProvince(){
if(true){
throw new RuntimeException();
}
return null;
}
Karena class Memento hanya digunakan oleh EmployeeProfile, maka class tersebut kita enkapsulasi di dalam class EmployeeProfile. Lalu kita menggunakan Map sebagai Caretaker untuk mengatur savepoint dan rollback. Kita tinggal memanggil method savepoint untuk menyimpan state tertentu dan restore untuk kembali ke savepoint yang pernah disimpan.
Kenapa menggunakan Memento Design Pattern?
Dengan Memento kita bisa menyimpan history state dari sebuah objek pada savepoint tertentu. Lalu setelah beberapa perubahan kita bisa kembali ke savepoint tertentu dan mengembalikan state objek tersebut lewat sebuah method. Memento menyederhanakan client code karena history objek dihandle oleh Caretaker objek.
Verdict
Memento Design Pattern bisa menyimpan dan mengembalikan state suatu objek. Ciri-ciri Memento Design Pattern adalah memiliki class untuk menyimpan histori state suatu objek dan Caretaker objek yang bertugas mengatur savepoint dan rollback state suatu objek. Oh ya, walaupun objeknya mutable, tapi objek Memento-nya sebaiknya dibikin immutable agar objek yang disimpan sebagai savepoint datanya aman dari mutasi. Dan Caretaker-nya sebaiknya menggunakan Concurrent objek agar thread-safe.