Prinsip Open Closed pada OOP


Dalam perancangan aplikasi yang dikembangkan dengan pendekatan berbasis objek, pada umumnya mengacu pada prinsip SOLID. Setiap huruf dari SOLID mewakili sebuah prinsip perancangan OOP, yaitu Single Responsible, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion. Tujuan dengan memahami 5 prinsip perancangan OOP tersebut akan memaksa kita untuk dapat mengoptimalkan penggunaan bahasa pendukung Object Oriented, seperti Java, C++, C#, Python, dan sebagainya, dalam aplikasi yang dikembangkan. Saya akan memulai membahas prinsip Open/Closed berdasar apa yang saya pahami dari beberapa sumber, sekaligus contoh sederhana dalam bahasa pemrograman Java.

Prinsip Open/Closed menyatakan “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”. Prinsip ini memaksa kita untuk dapat merancang sebuah entitas, misalnya class, yang dapat dimodifikasi perilakunya (misalnya, apa yang dikerjakan oleh sebuah method) tanpa harus mempermak source code-nya yang sudah ditetapkan sebelumnya. Maksudnya, sebuah entitas sebaiknya bersifat terbuka untuk diperluas, sehingga sebuah perilaku (method) dapat diubah dengan cara menambahkan kode program baru. Sedangkan untuk kode sumber yang sudah ada, sedapat mungkin tertutup atau setidaknya jarang sekali untuk perlu memodifikasi source code perilaku yang sudah ada.

Untuk dapat memahami prinsip ini, berikut saya membuatkan sebuah contoh sederhana yang tidak mengikuti prinsip Open/Closed dalam bahasa Java:

package com.wordpress.budsus.oop;

public enum TipeProduk {
   MAJALAH, BUKU, TABLOID
}

 

package com.wordpress.budsus.oop;

public class InfoProduk {
	public String nomor;
	public String judul;
	public int harga;
	public int stok;
}

 

package com.wordpress.budsus.oop;

public class Produk {
	private InfoProduk produk;

	public TipeProduk tipeProduk;

	public Produk(InfoProduk prod) {
		this.produk = prod;
	}

	public void minusStok(int jml) {
		this.produk.stok -= jml;
	}

	public void plusStok(int jml) {
		this.produk.stok += jml;
	}

	public int getStok() {
		return this.produk.stok;
	}

	public double hitDiscount(){
		double hsl = 0.0;
		switch (tipeProduk) {
			case MAJALAH:
				hsl = this.produk.harga*(0.90);
				break;
			case BUKU:
				hsl = this.produk.harga*(0.85);
				break;
			case TABLOID:
				hsl = this.produk.harga*(0.95);
				break;
		}
		return hsl;
	}
}

Dari kode program tersebut, satu hal yang perlu diperhatikan sebagai sebuah pelanggaran prinsip Open/Closed adalah method  hitDiscount(). Jika ternyata ada perubahan perilaku terhadap discount terhadap tiap tipe produk, maka yang perlu dilakukan adalah memodifikasi method tersebut keseluruhan. Kemudian hal lain yang dapat terjadi adalah jika terdapat tipe produk baru yang muncul. Maka kode program harus dimodifikasi enumerator TipeProduk dan juga perubahan pada method hitDiscount().

Agar dapat mengikuti prinsip Open/Closed, maka class Produk harus dimodifikasi, terutama method hitDiscount(), dan sebaiknya membuang tipe enumatornya. Agar dapat mengubah menjadi class Produk yang dapat diperluas sesuai dengan tipe produknya, maka class Produk sebaiknya diubah menjadi class abstrak. Berikut adalah contoh perubahan class Produk, dan pembuatan class Majalah, class Tabloid, dan class Buku.

package com.wordpress.budsus.oop;

public abstract class Produk {
	protected InfoProduk produk;

	public TipeProduk tipeProduk;

	public Produk(InfoProduk prod) {
		this.produk = prod;
	}

	public void minusStok(int jml) {
		this.produk.stok -= jml;
	}

	public void plusStok(int jml) {
		this.produk.stok += jml;
	}

	public int getStok() {
		return this.produk.stok;
	}

	public abstract double hitDiscount();
}

 

package com.wordpress.budsus.oop;

public class Majalah extends Produk {

	public Majalah(InfoProduk produk) {
		super(produk);
	}

	@Override
	public double hitDiscount() {
		return this.produk.harga*(0.90);
	}

}

 

package com.wordpress.budsus.oop;

public class Buku extends Produk {

	public Buku(InfoProduk produk) {
		super(produk);
	}

	@Override
	public double hitDiscount() {
		return this.produk.harga * (0.85);
	}

}

 

package com.wordpress.budsus.oop;

public class Tabloid extends Produk {

	public Tabloid(InfoProduk produk) {
		super(produk);
	}

	@Override
	public double hitDiscount() {
		return this.produk.harga * (0.85);
	}

}

Saya dapat menguji class tersebut di atas dengan membuat sebuah Unit Test Class sebagai berikut :

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;
import com.wordpress.budsus.oop.*;

public class TestProduk {

	@Before
	public void setUp() throws Exception {
	}

	@Test
	public void test() {
		InfoProduk ip = new InfoProduk();
		ip.nomor = "1111";
		ip.judul = "Perancangan OOP";
		ip.harga = 20000;
		ip.stok = 10;

		Majalah m = new Majalah(ip);
		m.plusStok(5);

		assertEquals("Stok adalah 15!", new Integer(15), new Integer(m.getStok()));
		assertEquals("Harga setelah discount adalah 18000!", new Double(18000), new Double(m.hitDiscount()));
	}

}