![Contoh Bridge Design Pattern](https://images.ctfassets.net/7wjlcr9d5ii8/284uSrJ5YgS5Nsng6tVWtO/72754032f7ada9f5eb224877e21d0f86/47537711841_b567bfc4f1_o.jpg?w=750&h=500&fl=progressive&q=100&fm=jpg&fit=thumb&f=faces)
Bridge Design Pattern termasuk salah satu design pattern yang agak kompleks dibanding design pattern yang lain. Penggunaan design pattern ini juga tidak terlalu populer. Design pattern ini biasanya digunakan untuk memisahkan hierarki sebuah class menjadi lebih independen sehingga pengimplementasiannya bisa lebih spesifik dan fleksibel.
Bridge Design Pattern adalah Structural Design Pattern yang memisahkan sebuah class yang lebih besar atau saling berkaitan menjadi beberapa hierarki yang berbeda sehingga masing-masing class bisa dikembangkan secara independen satu sama lain.
Design Pattern
Kita akan membuat aplikasi yang ingin menampilkan komposisi mie.
- Dibutuhkan beberapa jenis mie seperti mie goreng dan mie rebus;
- Masing-masing mie tersebut memiliki topping seperti telur dan bakso yang interchangeable;
Noodle Interface
public interface Noodle{
void printNoodles();
}
BoildeNoodle Abstract Class
public abstract class BoiledNoodle implements Noodle{
@Override
public void printNoodles(){
System.out.println(getDescription());
}
protected String getDescription(){
return "This is the delicious Boiled Noodle";
}
}
BoiledNoodleWithEgg Class
public class BoiledNoodleWithEgg extends BoiledNoodle{
@Override
public void printNoodles(){
System.out.println(getDescription() + " with sunny side up");
}
}
BoiledNoodleWithMeatBall Class
public class BoiledNoodleWithMeatball extends BoiledNoodle{
@Override
public void printNoodles(){
System.out.println(getDescription() + " with meatball");
}
}
FriedNoodle Abstract Class
public abstract class FriedNoodle implements Noodle{
@Override
public void printNoodles(){
System.out.println(getDescription());
}
protected String getDescription(){
return "Fried Noodle is the best noodle";
}
}
FriedNoodleWithEgg Class
public class FriedNoodleWithEgg extends FriedNoodle{
@Override
public void printNoodles(){
System.out.println(getDescription() + " with sunny side up");
}
}
FriedNoodleWithMeatBall Class
public class FriedNoodleWithMeatball extends FriedNoodle{
@Override
public void printNoodles(){
System.out.println(getDescription() + " with meatball");
}
}
Contoh Penggunaan
public static void main(String[] args){
Noodle boiledNoodleWithEgg = new BoiledNoodleWithEgg();
boiledNoodleWithEgg.printNoodles();
System.out.println();
Noodle friedNoodleWithEgg = new FriedNoodleWithEgg();
friedNoodleWithEgg.printNoodles();
System.out.println();
Noodle boiledNoodleWithMeatBall = new BoiledNoodleWithMeatball();
boiledNoodleWithMeatBall.printNoodles();
}
Kita coba menggunakan pendekatan seperti Template Method Design Pattern seperti sebelumnya. Kita menggunakan inheritance untuk membuat hierarki dari Noodle hingga Noodle beserta tipe dan toppingnya masing-masing.
Dari pertama liat mungkin udah pada tau masalahnya😅. Benar, code di atas cukup kompleks. Menggunakan inheritance untuk menggunakan kembali sebuah code memang bad practice. Hierarki class di atas susah untuk dikembangkan ketika terjadi perubahan. Misalnya ada penambahan tipe mie seperti mie tumis, atau penambahan jenis topping seperti keju, maka makin kompleks lagi nanti hierarkinya. Kita jadi bakal bikin pasangan-pasangan class tersebut setiap perubahan. Jadi Template Method Design Pattern bukan solusi untuk kasus yang satu ini.
Sekarang mari liat Bridge Design Pattern😎.
Noodle Interface
public interface Noodle{
void printNoodles();
}
Topping Interface
public interface Topping{
String getName();
}
BoiledNoodle Class
public class BoiledNoodle implements Noodle{
private final Topping topping;
public BoiledNoodle(Topping topping){
this.topping = topping;
}
@Override
public void printNoodles(){
System.out.println("This is the delicious Boiled Noodle with " + topping.getName());
}
}
FriedNoodle Class
public class FriedNoodle implements Noodle{
private final Topping topping;
public FriedNoodle(Topping topping){
this.topping = topping;
}
@Override
public void printNoodles(){
System.out.println("Fried Noodle is the best noodle with " + topping.getName());
}
}
EggTopping Class
public class EggTopping implements Topping{
@Override
public String getName(){
return "sunny side up";
}
}
MeatBallTopping Class
public class MeatBallTopping implements Topping{
@Override
public String getName(){
return "Malang Meatball";
}
}
Contoh Penggunaan
public static void main(String[] args){
Noodle boiledNoodleWithEgg = new BoiledNoodle(new EggTopping());
boiledNoodleWithEgg.printNoodles();
System.out.println();
Noodle boiledNoodleWithMeatBall = new BoiledNoodle(new MeatBallTopping());
boiledNoodleWithMeatBall.printNoodles();
System.out.println();
Noodle friedNoodleWithEgg = new FriedNoodle(new EggTopping());
friedNoodleWithEgg.printNoodles();
}
Nah, sekarang antara mie dan topping kita pisah hierarkinya. Kita menghubungkan antara mie dan topping lewat composition, bukan inheritance lagi. Dengan begitu lebih interchangeable ketika dipasang-pasangkan. Selanjutnya setiap penambahan tipe mie atau topping tinggal implementasi class baru secara independen. User nantinya yang menentukan tipe mie dan topping apa yang diinginkan ketika melakukan pembuatan instance object.
Dengan Bridge Design Pattern, kita memecah monolitik class menjadi beberapa varian. Kita bisa mengimplementasi Bridge Design Pattern ketika beberapa dimensi class diidentifikasi cukup complex seperti permasalah di atas. Dengan Bridge Design Pattern, user bisa menentukan implementasi yang diinginkan saat runtime dengan melakukan dependency injection. Tapi, penggunaan dengan objek yang memiliki kohesi yang cukup tinggi juga bisa mengakibatkan code menjadi kompleks. Misalnya selain topping, kita juga ingin menambahkan merk mie, rasa mie, dan sebagainya, sehingga dependency-nya juga bisa menjadi lebih besar lagi.
Intinya dengan Bridge Design Pattern kita bisa memecah sebuah class yang besar dengan hierarki yang nantinya cukup kompleks menjadi beberapa varian kecil yang interchangeable sehingga perubahannya dilakukan secara independen. Code kita jadi mengimplementasi Open/Closed Principle. Gw sendiri pernah melakukan pendekatan ini ketika melakukan refactor legacy code yang menggunakan inheritance untuk menggunakan kembali beberapa behavior. Tentu saja itu cukup menyulitkan mocking object ketika membuat unit testing. Dengan Bridge dan Adapater Design Pattern gw bisa menghubungkan legacy class dan mengkonversinya menjadi objek baru secara composition sehingga lebih mudah dikembangkan.