• Cts. Eyl 18th, 2021

Bridge Design Pattern Ve Java İle Örneklemesi

Merhabalar.

Bu yazıda Bridge Design Pattern‘den ve kullanım şeklinden bahsedeceğiz.

Öncelikle bu tasarım kalıbının ortaya çıkışında etken olan soruna değinelim ve kod örneğimizi de bu sorun üzerinden yapalım.

Diyelim ki biz hem uzaktan kumanda ile yönetilebilen araçlar hem de bunların uzaktan kumandalarını üreten bir projede çalışıyoruz. Örneğin en tepede ortak özelliklerin toplandığı bir Device sınıfımız olsun. Bu Device sınıfından bir TV sınıfı ürettiğimizi düşünelim. Ayrıca bu TV aygınıtını uzaktan yönetmek için de TV sınıfı ile ilişkili bir RemoteTV sınıfına ihtiyacımız olacaktır.

Burası şimdilik tamam.

Daha sonra ürüne Radio için eklemeler yapılması talebi geldi. Biz yine Device sınıfından kalıtarak Radio sınıfını türettik ve yine uzaktan yönetmek için RemoteRadio sınıfını yazdık.

Burası da tamam.

Daha sonra ürüne uzaktan kumandalı araba özelliği eklenmesi talebi geldiğini düşünelim. Ne yapacağız? Yine gidip Device sınıfından ChildCar sınıfını türeteceğiz ve uzaktan yönetmek için de RemoteChildCar sınıfını yazacağız.

Buradaki sıkıntı bu noktada başlıyor. Her gelen yeni talep için böyle yeni sınıflar yazarak, eklemeler yaparak yönetilmesi zor bir koda doğru yönlendiriyoruz projemizi. Kodun çok dallanması geliştirilebilme özelliğini azaltıyor ve SOLID prensiplerinden Open/Closed prensibini uygulayamama yoluna da götürüyor.

İşte bu noktada her yeni gelen talep için sürekli implementasyon yapmak zorunda kalıyorsak, hatta client’ımızı da değişiklik yapmak zorunda bırakıyorsak client’ın kendi tarafında bize dokunmadan, bizim de client’a dokunmadan geliştirme yapabilmemiz için Bridge Desing Pattern’i kullanabiliriz.

Bu noktada soyut kısımları ve iş yapan kısımları birbirinden ayıracak, bu iki kısım arasında köprü görevi görecek bir interface ile bu işlemi halletmeye çalışacağız. Bu çalışma ile iki tarafın birbirini etkilemeden geliştirme yapılabilmesini hedefliyoruz diğer taraftan.

Şimdi kodlayarak örnekleyelim Bridge Pattern’i.

Device.java

package com.ilkaygunel.bridge;

public interface Device {
    boolean isEnabled();

    void enable();

    void disable();

    int getVolume();

    void setVolume(int percent);

    int getChannel();

    void setChannel(int channel);

    void printStatus();
}

Device interfcace’i bizim TV, Radio gibi uzaktan kumanda edibelin araçlara ait sınıfları implement edeceğiz bir interface. Aynı zamanda köprü görevi görecek olan interface de bu interface.

Radio.java

package com.ilkaygunel.bridge;

public class Radio implements Device {
    private boolean on = false;
    private int volume = 30;
    private int channel = 1;

    @Override
    public boolean isEnabled() {
        return on;
    }

    @Override
    public void enable() {
        on = true;
    }

    @Override
    public void disable() {
        on = false;
    }

    @Override
    public int getVolume() {
        return volume;
    }

    @Override
    public void setVolume(int volume) {
        if (volume > 100) {
            this.volume = 100;
        } else if (volume < 0) {
            this.volume = 0;
        } else {
            this.volume = volume;
        }
    }

    @Override
    public int getChannel() {
        return channel;
    }

    @Override
    public void setChannel(int channel) {
        this.channel = channel;
    }

    @Override
    public void printStatus() {
        System.out.println("------------------------------------");
        System.out.println("| I'm radio.");
        System.out.println("| I'm " + (on ? "enabled" : "disabled"));
        System.out.println("| Current volume is " + volume + "%");
        System.out.println("| Current channel is " + channel);
        System.out.println("------------------------------------\n");
    }
}

Radio sınıfımız Device interface’ini implement eden ve onun içerisinden metotların gövdelerini dolduran sınıftır.

TV.java

package com.ilkaygunel.bridge;

public class Tv implements Device {
    private boolean on = false;
    private int volume = 30;
    private int channel = 1;

    @Override
    public boolean isEnabled() {
        return on;
    }

    @Override
    public void enable() {
        on = true;
    }

    @Override
    public void disable() {
        on = false;
    }

    @Override
    public int getVolume() {
        return volume;
    }

    @Override
    public void setVolume(int volume) {
        if (volume > 100) {
            this.volume = 100;
        } else if (volume < 0) {
            this.volume = 0;
        } else {
            this.volume = volume;
        }
    }

    @Override
    public int getChannel() {
        return channel;
    }

    @Override
    public void setChannel(int channel) {
        this.channel = channel;
    }

    @Override
    public void printStatus() {
        System.out.println("------------------------------------");
        System.out.println("| I'm TV set.");
        System.out.println("| I'm " + (on ? "enabled" : "disabled"));
        System.out.println("| Current volume is " + volume + "%");
        System.out.println("| Current channel is " + channel);
        System.out.println("------------------------------------\n");
    }
}

TV sınıfımız da aynı şekilde Device interface’ini uygulayan ve metotların içini dolduran bir sınıf.

Remote.java

package com.ilkaygunel.bridge;

public interface Remote {
    void power();

    void volumeDown();

    void volumeUp();

    void channelDown();

    void channelUp();
}

Remote arabirimimiz uzaktan kumanda fonksiyonlarının tanımlandığı birim oluyor bizim için.

BasicRemote.java

package com.ilkaygunel.bridge;

public class BasicRemote implements Remote {
    protected Device device;

    public BasicRemote() {}

    public BasicRemote(Device device) {
        this.device = device;
    }

    @Override
    public void power() {
        System.out.println("Remote: power toggle");
        if (device.isEnabled()) {
            device.disable();
        } else {
            device.enable();
        }
    }

    @Override
    public void volumeDown() {
        System.out.println("Remote: volume down");
        device.setVolume(device.getVolume() - 10);
    }

    @Override
    public void volumeUp() {
        System.out.println("Remote: volume up");
        device.setVolume(device.getVolume() + 10);
    }

    @Override
    public void channelDown() {
        System.out.println("Remote: channel down");
        device.setChannel(device.getChannel() - 1);
    }

    @Override
    public void channelUp() {
        System.out.println("Remote: channel up");
        device.setChannel(device.getChannel() + 1);
    }
}

BacicRemote sınıfımız Remote arabirimini uyguluyor ve ondan gelen metotların içlerini dolduruyor.

Bu sınıfta dikkat etmemiz gereken 2 nokta var. Birincisi sınıf içerisinde Device tipinde tanımlanmış bir obje mevcut. İkincisi bu sınıfın parametreli yapılandırıcısı Device tipinde bir parametre alıp bu sınıf içerisinde objeye atıyor. Remote arabiriminden gelen metotların içleri doldurulurken de bu device objesi üzerinden çağırım yapılıyor. Örneğin Remote interface’inden gelen power() metodu device objesi üzerinden cihazı açma ya da kapama işlemini gerçekleştiriyor. Bu noktada Device soyutlanmış durumda. Biz onun hangi Device olduğunu bilmiyoruz.Yani aslında araç ve kumanda kısmı birbirinden ayrılmış hale gelmiş oluyorlar. Öte yandan TV için ayrı, Radio için ayrı, kumandalı araba için ayrı sınıflar yazma ihtiyacı yok, tek sınıf ile halledebiliyoruz.

AdvancedRemote.java

package com.ilkaygunel.bridge;

public class AdvancedRemote extends BasicRemote {

    public AdvancedRemote(Device device) {
        super.device = device;
    }

    public void mute() {
        System.out.println("Remote: mute");
        device.setVolume(0);
    }
}

AdvancedRemote sınıfımız da BasicRemote sınıfını kalıtıp BasicRemote’dan farklı olarak bir de mute özelliği sunuyor. Yine soyut durumdaki Device’ın setVolume bilgisi kullanılıyor.

MainClass.java

package com.ilkaygunel.bridge;

public class MainClass {
    public static void main(String[] args) {
        testDevice(new Tv());
        testDevice(new Radio());
    }

    public static void testDevice(Device device) {
        System.out.println("Tests with basic remote.");
        BasicRemote basicRemote = new BasicRemote(device);
        basicRemote.power();
        device.printStatus();

        System.out.println("Tests with advanced remote.");
        AdvancedRemote advancedRemote = new AdvancedRemote(device);
        advancedRemote.power();
        advancedRemote.mute();
        device.printStatus();
    }
}

MainClass içerisinde main metodumuz ve Device tipinde parametre kabul eden testDevice metodumuz yer alıyor. main metot içerisinden testDevice metodunu TV ve Radio tipindeki objeler geçirerek çağırıyoruz. testDevice metodu gelen parametre ile bir BasicRemote ve bir de AdvancedRemote nesneleri oluşturuyoruz. BasicRemote objesi üzerinden power ve printStatus metotlarını çağırıyoruz. Akabinde AdvancedRemote nesnesi üzerinden power, mute ve printStatus metotlarını çağırıyoruz. Önce TV sınıfına ait metotların çağırımı, sonrasında da Radio sınıfına ait metotların çağırımı gerçekleştirilecek.

Konsol Çıktısı

Tests with basic remote.
Remote: power toggle
------------------------------------
| I'm TV set.
| I'm enabled
| Current volume is 30%
| Current channel is 1
------------------------------------

Tests with advanced remote.
Remote: power toggle
Remote: mute
------------------------------------
| I'm TV set.
| I'm disabled
| Current volume is 0%
| Current channel is 1
------------------------------------

Tests with basic remote.
Remote: power toggle
------------------------------------
| I'm radio.
| I'm enabled
| Current volume is 30%
| Current channel is 1
------------------------------------

Tests with advanced remote.
Remote: power toggle
Remote: mute
------------------------------------
| I'm radio.
| I'm disabled
| Current volume is 0%
| Current channel is 1
------------------------------------

Konsol çıktısından görebileceğimiz gibi önce TV sınıfından çağırımlar yapılıyor, ardından da Radio sınıfından çağırımlar yapılıyor.

NOT: Yazının kapak görseli https://refactoring.guru/images/patterns/content/bridge/bridge.png?id=bd543d4fb32e11647767 adresinden alınmıştır ve bu yazıda https://refactoring.guru adresinden yararlanılmıştır.

2016 İstanbul Üniversitesi Bilgisayar Mühendisliği mezunuyum. Güncel olarak Paycore'da SoftPOS projesinde Java Developer olarak çalışmaktayım. Büyük oranda Java teknolojileri ile uğraşıp kendimi geliştirmeye çalışıyorum.

İlkay Günel

2016 İstanbul Üniversitesi Bilgisayar Mühendisliği mezunuyum. Güncel olarak Paycore'da SoftPOS projesinde Java Developer olarak çalışmaktayım. Büyük oranda Java teknolojileri ile uğraşıp kendimi geliştirmeye çalışıyorum.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir