author-pic

Ferry S

An ISTJ, Type 5, Engineer, Gamer, and Thriller-Movies-Lover
Contoh Abstract Factory Design Pattern
Mon. Oct 4th, 2021 03:22 PM5 mins read
Contoh Abstract Factory Design Pattern
Source: Depositphotos - 253,945 Factory Vector Images, Factory Illustrations

Sebelumnya kita udah membahas Factory Method Design Pattern. Kalau belum baca, mending dibaca dulu biar nyambung dengan tulisan ini karena use case-nya masih sambungan dari tulisan tersebut🙂. Sekarang kita bahas versi upgrade-nya, yaitu Abstract Factory Design Pattern. Keduanya memang mirip, yaitu memberikan user kebebasan memilih implementasi objek apa yang dipakai secara runtime lewat input. Bedanya Abstract Factory Design Pattern ga hanya memberikan implementasi objeknya saja, tapi juga satu set family objek terkait. Makanya bisa dibilang ini adalah Factory Method versi upgrade. Oh ya, jika masih ambigu tentang terminologi "Factory" dalam programming, bisa baca artikel Macam-macam Factory terlebih dahulu.

Abstract Factory Design Pattern adalah Creational Design Pattern yang memberikan implementasi objek beserta family objek terkait.

Design Pattern

Untuk mempermudah pembahasan, kita masih menggunakan use case yang sama seperti Factory Method dengan beberapa requirement tambahan:

  • Terdapat 2 jenis laptop, Asus dan Dell;
  • Masing-masing laptop memiliki Gaming Laptop dan Office Laptop;
  • Gaming Laptop menggunakan Nvidia Graphic Card dan RGB Keyboard;
  • Office Laptop menggunakan Intel Graphic Card dan Standard Keyboard;
  • Ketika user menginginkan Gaming Laptop dengan input "asus" maka yang didapatkan adalah Asus Rog;
  • Ketika user menginginkan Gaming Laptop dengan input "dell" maka yang didapatkan adalah Dell Alienware;
  • Ketika user menginginkan Office Laptop dengan input "asus" maka yang didapatkan adalah Asus Vivobook;
  • Ketika user menginginkan Office Laptop dengan input "dell" maka yang didapatkan adalah Dell Latitude;
  • Ketika user memberikan input yang salah maka tampilkan error;

Contoh code-nya kurang lebih sama seperti contoh pada Factory Method Design Pattern sebelumnya, karena menggunakan use case yang mirip. Jadi untuk menghemat tulisan code Laptop dan implementasinya ga gw tulis. Gw hanya menuliskan class tambahannya beserta contoh method dan contoh penggunaannya aja ya😅.

Interface LaptopGraphicCard

public interface LaptopGraphicCard{
	String getGraphic();
}

Class GamingGraphicCard

public class GamingGraphicCard implements LaptopGraphicCard{
	@Override
	public String getGraphic(){
		return "NVIDIA";
	}
}

Class OfficeGraphicCard

public class OfficeGraphicCard implements LaptopGraphicCard{
	@Override
	public String getGraphic(){
		return "Intel";
	}
}

Interface LaptopKeyboard

public interface LaptopKeyboard{
	String getKeyboard();
}

Class GamingKeyboard

public class GamingKeyboard implements LaptopKeyboard{
	@Override
	public String getKeyboard(){
		return "RGB";
	}
}

Class OfficeKeyboard

public class OfficeKeyboard implements LaptopKeyboard{
	@Override
	public String getKeyboard(){
		return "Standard";
	}
}

Method buildLaptop()

public static Laptop buildLaptop(String laptop, String type){
	String key = laptop + "-" + type;
	if("asus-gaming".equals(key)){
		return new AsusRog(new GamingGraphicCard(), new GamingKeyboard());
	}
	if("dell-gaming".equals(key)){
		return new DellAlienware(new GamingGraphicCard(), new GamingKeyboard());
	}
	if("asus-office".equals(key)){
		return new AsusVivoBook(new OfficeGraphicCard(), new OfficeKeyboard());
	}
	if("dell-office".equals(key)){
		return new DellLatitude(new OfficeGraphicCard(), new OfficeKeyboard());
	}
	throw new IllegalArgumentException("not found");
}

Contoh penggunaan

public static void main(String[] args){
	Laptop laptop = buildLaptop("dell", "gaming");
	System.out.println("dell.getLaptopName() = " + laptop.getLaptopName());
}

Untuk mempersingkat tulisan, gw menulis parameternya menggunakan String, tapi baiknya case seperti di atas menggunakan Enum parameter pada Factory.

Seperti yang pernah dibahas sebelumnya, jika dibuat tanpa Design Pattern maka method buildLaptop() akan semakin gendut seiring bertambahnya requirement seperti jenis laptop. Lalu maintenance property seperti Keyboard dan Graphic Card juga akan semakin kompleks kedepannya apabila ada penambahan property lainnya seperti Processor, RAM, Monitor, dan lainnya.

Sekarang kita improve menggunakan Abstract Factory Design Pattern😎.

Interface Laptop

interface Laptop{
	String getLaptopName();
	String getKeyboardType();
	String getGraphicType();
}

Abstract Class Laptop

public abstract class AbstractLaptop implements Laptop{
	protected final LaptopProperty property;

	protected AbstractLaptop(LaptopProperty property){
		this.property = property;
	}
}

Class AsusRog

public class AsusRog extends AbstractLaptop{

	public AsusRog(LaptopProperty property){
		super(property);
	}

	@Override
	public String getLaptopName(){
		return "Asus ROG Zephyrus";
	}

	@Override
	public String getKeyboardType(){
		return property.getLaptopKeyboard().getKeyboard();
	}

	@Override
	public String getGraphicType(){
		return property.getLaptopGraphicCard().getGraphic();
	}
}

Class DellAlienware

public class DellAlienware extends AbstractLaptop{

	public DellAlienware(LaptopProperty property){
		super(property);
	}

	@Override
	public String getLaptopName(){
		return "Dell AlienWare M15";
	}

	@Override
	public String getKeyboardType(){
		return property.getLaptopKeyboard().getKeyboard();
	}

	@Override
	public String getGraphicType(){
		return property.getLaptopGraphicCard().getGraphic();
	}
}

Class AsusVivobook

public class AsusVivobook extends AbstractLaptop{

	public AsusVivobook(LaptopProperty property){
		super(property);
	}

	@Override
	public String getLaptopName(){
		return "Asus Vivobook 14";
	}

	@Override
	public String getKeyboardType(){
		return property.getLaptopKeyboard().getKeyboard();
	}

	@Override
	public String getGraphicType(){
		return property.getLaptopGraphicCard().getGraphic();
	}
}

Class DellLatitude

public class DellLatitude extends AbstractLaptop{

	public DellLatitude(LaptopProperty property){
		super(property);
	}

	@Override
	public String getLaptopName(){
		return "Dell Latitude 7490";
	}

	@Override
	public String getKeyboardType(){
		return property.getLaptopKeyboard().getKeyboard();
	}

	@Override
	public String getGraphicType(){
		return property.getLaptopGraphicCard().getGraphic();
	}
}

Interface LaptopProperty

public interface LaptopProperty{
	LaptopGraphicCard getLaptopGraphicCard();
	LaptopKeyboard getLaptopKeyboard();
}

Class GamingLaptopProperty

public class GamingLaptopProperty implements LaptopProperty{
	@Override
	public LaptopGraphicCard getLaptopGraphicCard(){
		return new GamingGraphicCard();
	}

	@Override
	public LaptopKeyboard getLaptopKeyboard(){
		return new GamingKeyboard();
	}
}

Class OfficeLaptopProperty

public class OfficeLaptopProperty implements LaptopProperty{
	@Override
	public LaptopGraphicCard getLaptopGraphicCard(){
		return new OfficeGraphicCard();
	}

	@Override
	public LaptopKeyboard getLaptopKeyboard(){
		return new OfficeKeyboard();
	}
}

Interface LaptopFactory

public interface LaptopFactory{
	Laptop buildLaptop(String laptop);
}

Class GamingLaptopFactory

public class GamingLaptopFactory implements LaptopFactory{
	@Override
	public Laptop buildLaptop(String laptop){
		LaptopProperty property = new GamingLaptopProperty();
		if("asus".equals(laptop)){
			return new AsusRog(property);
		}
		if("dell".equals(laptop)){
			return new DellAlienware(property);
		}
		throw new IllegalArgumentException("not found");
	}
}

Class OfficeLaptopFactory

public class OfficeLaptopFactory implements LaptopFactory{
	@Override
	public Laptop buildLaptop(String laptop){
		LaptopProperty property = new OfficeLaptopProperty();
		if("asus".equals(laptop)){
			return new AsusVivobook(property);
		}
		if("dell".equals(laptop)){
			return new DellLatitude(property);
		}
		throw new IllegalArgumentException("not found");
	}
}

Contoh penggunaan

public static void main(String[] args){
	LaptopFactory laptopFactory = new OfficeLaptopFactory();
	Laptop laptop = laptopFactory.buildLaptop("asus");
	System.out.println("laptop.getLaptopName() = " + laptop.getLaptopName());
	System.out.println("laptop.getGraphicType() = " + laptop.getGraphicType());
	System.out.println("laptop.getKeyboardType() = " + laptop.getKeyboardType());
}

Setelah kita apply Abstract Factory Design Pattern, semuanya jadi lebih independent dan fleksibel, baik itu Laptop, LaptopFactory, hingga LaptopProperty. Method sebelumnya dipecah menjadi beberapa class Factory, yaitu GamingLaptopFactory dan OfficeLaptopFactory. Graphic Card dan Keyboard dibungkus ke dalam LaptopProperty yang digunakan oleh Laptop secara komposisi. Untuk "memaksa" turunan Laptop menggunakan LaptopProperty sebagai komposisi, kita perlu membuat AbstractLaptop dan menambahkan constructor yang menampung LaptopProperty. LaptopProperty dapat di-inject lewat masing-masing factory sesuai family objek terkait, dalam hal ini GamingLaptopProperty ke GamingLaptopFactory, dan OfficeLaptopProperty ke OfficeLaptopFactory. Ketika kita menginputkan "asus" dengan GamingLaptopProperty, maka akan menghasilkan objek asus gaming dengan gaming property. Begitu juga ketika kita menggunakan OfficeLaptopFactory, maka akan menghasilkan objek asus dengan office property. Ketika ada penambahan jenis laptop atau jenis property, tinggal bikin implementasi baru tanpa mengubah class yang sudah ada.

Secara pemakaian biasanya seorang software engineer menggunakan pendekatan Factory Method terlebih dahulu. Jika ditemukan case yang kompleks dan diselesaikan menggunakan komposisi, barulah menggunakan AbstractFactory. Biasanya ditandai dengan adanya beberapa komposisi dengan satu set family objek yang mirip, seperti pada contoh di atas adalah Graphic Card dan Keyboard, sehingga family objek tersebut bisa dipecah. Gaming Graphic Card & Gaming Keyboard untuk Gaming Laptop, dan Office Graphic Card & Office Keyboard untuk Office Laptop. Dengan begitu masing-masing objek jadi lebih spesifik beserta satu set komposisi family objek terkait.

Abstract Factory Design Pattern merupakan versi upgrade dari Factory Method Design Pattern. Keduanya memiliki tujuan yang sama, yaitu memberikan implementasi objek sesuai input user. Bedanya, jika Factory Method hanya berfokus pada inheritance, maka Abstract Factory juga berfokus pada composition. Makanya Abstract Factory lebih rumit daripada Factory Method😵. Biasanya design pattern ini baru dipertimbangkan setelah Factory Method Design Pattern sudah terlalu kompleks dan berkembang memiliki beberapa composition yang mempunyai kemiripan sehingga dapat dipecah sesuai family objek terkait. Makanya Abstract Factory ini tidak sepopuler saudaranya, Factory Method.