Pengenalan Konsep Observer Design Pattern (I)

Observer pattern merupakan salah satu Software Design Pattern yang berguna untuk memantain state (keadaan) dari sesuatu dan mengabarkan state tersebut kepada bagian-bagian yang membutuhkan.  Saya akan menjelaskan gambaran lebih detail tentang design pattern ini dan contoh implementasinya dalam Java. Tapi, sebelum itu, saya akan coba memberikan gambaran konseptual mengenai observer pattern.

Menjelang lebaran, Budi ingin mencari baju kemeja warna biru merk “harvest” di toko baju terdekat. Ketika ia datang, ke toko tersebut, ternyata yang ada baju warna merah. Selang 1 minggu, ia pun kembali lagi datang ke toko tersebut, tetapi stok baju biru belum tersedia. Beberapa hari berikutnya, ia mengecek lagi, tetapi stok baju biru yang ia inginkan juga belum tersedia. Akhirnya, Budi memutuskan untuk menghubungi CS toko tersebut dan memberikan nomor hanphonenya. Ia memesan kepada CS tersebut untuk menghubungi Budi apabila stok baju biru yang diinginkannya sudah tersedia.

Skenario Budi ketika menghubungi CS untuk diberi informasi stok baju biru tersebut merupakan contoh gambaran mengenai apa itu Observer Pattern.

Bagaimana Observer Pattern ini bekerja?

Misalkan ada 3 customer, customer1, customer2, customer3. Mereka semua menginginkan baju biru dengan model/spesifikasi yang sama. Mereka menghubungi CS, untuk dan meminta agar CS melakukan ‘Notify’ kepada mereka apabila stok baju biru tersedia. Kemungkinan juga karena suatu hal, salah satu customer, misalnya, customer3, tidak jadi memesan, sehingga tidak jadi di ‘Notify’ ketika stok baru tersedia.

Design pattern ini bisa juga dikatakan sebagai publisher-subscriber, di mana customer bisa kita ibaratkan sebagai subsriber, dan CS kita ibaratkan sebagai publisher.
Dalam design pattern ini, ada 2 entitas yang saling berhubungan, yaitu Observer dan Subject. Hubungan antara kedua entitas ini adalah One To Many atau Many to One. Satu subjek dapat disubcribe oleh beberapa observer.

  • Semua customer yang menginginkan baju biru kita katakan sebagai: Observers
  • Sedangkan, objek yang diinginkan user, yakni baju biru tersebut kita katakan sebagai Subject. Dalam terminologi lain, Subject ini disebut juga Observable.

Ringkasnya, ada pemesan, ada yang dipesan. Pemesan adalah Observer, dan yang dipesan adalah Observable/Subject.

Berikut in adalah behaviour yang wajib dilakukan oleh Subject/Observable:

  • Register Observer: Observable dapat mendaftarkan observer yang akan melakukan subscribe
  • Notify Observer: Beri tahu observer ketika ada perubahan state
  • Remove Observer:  Unsubscibe dari observable (objek yang disubcribe)

Adapun behaviour yang dimiliki oleh Observer adalah:

  • Update: sesuatu yang dilakukan ketika mendapat notifikasi dari Observer

Berikut ini bentuk class diagram dari obversver pattern.

image1

Pertama, kita bahas behaviour Observable. Behaviour Observable dapat kita definisikan dalam sebuah interface

public interface Observable {

    void addObserver(Observer o);  
    void removeObserver(Observer o);  
    void notifyObserver();  
}

Mari kita implementasi skenario Budi mencari kemeja biru di atas dalam program Java

public class BlueDress implements Observable {
    private boolean inStock = true;
    private ArrayList<Observer> customers;

    public BlueDress(Boolean inStock) {
        this.inStock = inStock;
        customers = new ArrayList<Observer>();
    }

    public boolean isInStock(){
        return  inStock;
    }

    public void setInStock(boolean inStock){
        this.inStock = inStock;
        if(isInStock()){
            notifyObserver();
        }
    }

    @Override
    public void addObserver(Observer o) {
        customers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        customers.remove(o);
    }

    @Override
    public void notifyObserver() {
        for(int i=0; i< customers.size(); i++){
            customers.get(i).update();
        }
    }
}

Kelas BlueDress adalah representasi dari baju yang dicari Budi. Ia memiliki atribut inStock, artinya stok tersedia. Karena BlueDress merupakan objek yang disubscribe, berarti ia adalah Observable. Oleh karenanya, perlu mengimplementasi interface Observable.

Kemudian, untuk Observer, ia memiliki behaviour ‘update’. Artinya melakukan sesuatu ketika di-notify oleh Observable.

public interface Observer {
    public void update();
}

Observer dalam hal kasus ini adalah Budi (Customer). Maka dari itu kita bisa buat representasinya dalam sebuah class yang mengimplementasi interface Observer.

public class Customer implements Observer {

    private Observable observable;
    private String username;

    public Customer(Observable observable, String username){
        this.observable = observable;
        this.username = username;
    }

    @Override
    public void update() {
        System.out.println("New blue dress available.");
        buyDress();
    }


    private void buyDress(){
        System.out.println(username +" getting new blue dress.");
    }

    public void unsubscribe(){
        observable.removeObserver(this);
    }
}

Kelas Customer adalah representasi dari customer yang mencari baju biru. Ia memiliki nama, dan dapat melakukan unsubscribe kapan saja. Dalam contoh di atas, ketika BlueDress tersedia, maka method update akan dipanggil. Ketika itu, kita dapat menentukan apa yang dilakukan oleh Observer, misalnya melakukan pembelian baju tersebut. Pada kelas tersebut, cunstructor dibuat memiliki parameter objek Observable supaya dapat melakukan unsubscribe melalui objek customer/observer.

Testing

Untuk mengetahui bagaimana observer pattern ini bekerja, kita bisa melakukan testing dengan membuat beberapa objek customer(Observer) dan mendaftarkannya untuk sebuah BlueDress (Observable).

BlueDress blueDress = new BlueDress(true);

Customer customer1 = new Customer(blueDress, "Andy");
blueDress.addObserver(customer1);

Customer customer2 = new Customer(blueDress, "Henry");
blueDress.addObserver(customer2);

Implementasi di atas menjelaskan, ada dua customer yang mencari baju biru, Andi dan Henry. Ketika stok baju biru tersedia, ia ingin segera dikabari agar dapat langsung membeli.
Ketika baju biru tersedia,

blueDress.setInStock(true);

Maka, otomatis semua customer akan diberi tahu. Berikut output dari perintah di atas:

07-28 17:04:36.149  17082-17082/ixsanslabs.com.observerpattern D/OpenGLRenderer﹕ Enabling debug mode 0
07-28 17:04:40.329  17082-17082/ixsanslabs.com.observerpattern I/System.out﹕ New blue dress available.
07-28 17:04:40.329  17082-17082/ixsanslabs.com.observerpattern I/System.out﹕ Andy getting new blue dress.
07-28 17:04:40.329  17082-17082/ixsanslabs.com.observerpattern I/System.out﹕ New blue dress available.
07-28 17:04:40.329  17082-17082/ixsanslabs.com.observerpattern I/System.out﹕ Henry getting new blue dress.

Penggunaan Singleton Pattern di Java/Android

Pada tulisan kali kita akan membahas tentang salah satu Design Pattern yang cukup populer, yaitu Singleton.
Singleton merupakan salah satu dari Gang of Four design pattern yang tergolong relatif simple penggunaanya.

Dalam Java, singleton bisa dikatakan desain / solusi yang memungkinkan dalam JVM (Java Virtual Machine), hanya ada satu instance/objek dari kelas tertentu. Bisa dikatakan ‘global point access to the object’, yaitu ketika kita ingin agar mengakses objek dari suatu kelas hanya melalui jalur yang satu. Jadi begini, ketika kita membuat instance dari kelas A misalnya, maka akan dibuat alokasi satu instance tersebut di JVM. Jika kita membuat instance lagi dari kelas A, maka dibuat lagi alokasi satu instance di JVM, sehingga ada dua instance di JVM. Dua instance ini tentu berbeda dan tidak berkaitan satu sama lain. Artinya jika ada modifikasi satu instance, instance yang lain tidak akan ikut berubah.

Singleton digunakan ketika kita ingin memantain state di dalam suatu kelas. Dalam contoh di atas, jika kelas A memiliki suatu atribut, dan kita ingin atribut ini nilainya tetap selama berada di JVM dan hanya dapat diubah melalui satu jalur saja, maka kita dapat menggunakan singleton. Lebih jelasnya, mari kita praktekkan. Pada tulisan ini, saya ambil contoh di Android.

Misal, saya membuat sebuah kelas singleton bernama Logger.

public class Logger {
    private static Logger mInstance = null;
    private String mString;
    private Logger(){
        mString = "Hello";
    }
    public static Logger getInstance(){
        if(mInstance == null)
        {
            mInstance = new Logger();
        }
        return mInstance;
    }
    public String getString(){
        return this.mString;
    }
    public void setString(String value){
        mString = value;
    }
}

Sebagaimana bisa kita lihat di atas, singleton class, memiliki struktur sebagai berikut:
1) Static Member: merupakan instance dari singleton class
2) Private Constructor: digunakan untuk mencegah instaniasi singleton class
3) Static Public Method: menyediakan satu akses ke instance dari singleton class.

Selanjutnya, untuk mencobanya, saya buat ActivityA yang menampilkan nilai awal mString dari kelas Logger.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_a);

    //Menampilkan nilai string dari instance Singleton
    ((TextView)findViewById(R.id.text_view)).setText(Logger.getInstance().getString());


    findViewById(R.id.btn_next).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //Mengubah nilai string instance Logger dari ActivityA
            Logger.getInstance().setString("Hallo diubah dari ActivityA");
            Intent intent = new Intent(getApplicationContext(),ActivityB.class);
            startActivity(intent);

        }
    });

}

TextView pada ActivityA akan menampilkan nilai default yaitu “Hallo” sebagaimana instaniasi default. Kemudian, pada AcitivyA, terdapat sebuah button yang apabila diklik akan mengubah nilai mString dari singleton class Logger menjadi “Hallo diubah dari ActivityA”, lalu menjalankan intent menuju ActivityB. ActivityB ini akan menampilkan nilai mString dari instance kelas Logger, sebagai berikut:

//Menampilkan nilai string dari instance Logger yang telah diubah di ActivityA
textView = (TextView)findViewById(R.id.text_view2);
setTextValue(Logger.getInstance().getString());

Berikut ini hasil ketika aplikasi dijalankan.

Selection_102Selection_103

Ketika ActivityA dijalankan, nilai mString dari kelas Logger masih bernilai default, yaitu “Hello”. Nilai ini dapat diubah seperti kasus di atas. Ketika tombol diklik nilai mString diubah lalu nilai ini ditampilkan pada ActivityB.

Terlihat simple bukan? Lalu bagaimana penggunaan lainnya?

Biasanya singleton ini digunakan pada class yang ‘only one at this moment’, maksudnya, hanya satu instance dalam JVM pada satu waktu, karena repetisi penggunaanya agar tidak boros. Misalnya digunakan dalam kelas Logging, caching, atau database helper SQLite yang mana kita tahu bahwa untuk mengakses objek database SQLite, tidak bisa dilakukan konkruen pada 2 instance yang berbeda.

Singleton juga kadang bisa digunakan untuk komunikasi antar Activity di Android. Misalnya, contoh yang sederhana, pada kasus di atas, saya ingin menambahkan ActivityC, yang mana ketika Activity ini muncul, maka kita dapat mengubah nilai textView pada ActivityA dan ActivityB. Caranya dengan membuat singleton pattern untuk kedua activity tersebut.

Activity A:

public static ActivityA singleton;
public static ActivityA getInstance(){
    return singleton;
}
TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    singleton = this;

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //Menampilkan nilai string dari instance Singleton
    textView = (TextView)findViewById(R.id.text_view);


    findViewById(R.id.btn_next).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //Mengubah nilai string instance Singleton dari ActivityA
            Singleton.getInstance().setString("Hallo diubah dari ActivityA");
            Intent intent = new Intent(getApplicationContext(),ActivityB.class);
            startActivity(intent);
       }
    });

}

//public method untuk mengganti nilai textview menggunakan singleton
public void setTextValue(String textValue){
    textView.setText(textValue);
}

Activity B:

public static ActivityB singleton;
public static ActivityB getInstance(){
    if(null == singleton){
        singleton = new ActivityB();
    }
    return singleton;
}
TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sub);

    singleton = this;

    //Menampilkan nilai string dari instance Logger yang telah diubah di MainActivity
    textView = (TextView)findViewById(R.id.text_view2);
    setTextValue(Logger.getInstance().getString());

    findViewById(R.id.btn_next).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            Intent intent = new Intent(getApplicationContext(), ActivityC.class);
            startActivity(intent);
        }
    });

}

Activity C: Melakukan perubahan nilai textView pada ActivityA dan ActivityC.

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sub_activity2);


    ActivityA.getInstance().setTextValue("Nilai textView ActivityA diubah dari ActivityC");
    ActivityB.getInstance().setTextValue("Nilai textView ActivityB diubah dari ActivityC");
}

Hasilnya, ketika Activity C dijalankan, maka ketika kembali ke ActivityB dan ActivityA, nilai textView pada kedua Activity tersebut sudah berubah dari semula.