Bridge Tasarım Deseni Nedir?

Bu yazıda bridge tasarım deseni nedir sorusuna kolay anlaşılır örneklerle yanıt bulacak, PHP ve Python için örnek kodları inceleyerek desenin koda nasıl döküldüğünü göreceksiniz.


Bridge Tasarım Deseni Nedir?

Bu yazı Design Patterns/Tasarım Desenleri nedir? başlıklı yazı dizisinin bir parçasıdır.

Bu içerik ağırlıklı olarak refactoring.guru sitesindeki içeriğin tercümesi ve derlenmesinden oluşturulmuştur.

Tüm tasarım desenleri ya da diğer adıyla tasarım kalıplarına yönelik ayrıntılı içeriklere yazının sonundaki bağlantılardan ulaşabilirsiniz.

Bridge tasarım deseninin amacı

Bridge, Türkçe karşılığı ile köprü büyük sınıfları veya birbiriyle yakın ilişkili sınıfları, birbirinden bağımsız olarak geliştirilebilecek iki ayrı hiyerarşiye bölmenizi sağlayan bir tasarım desenidir.

Sorun

Abstraction (Soyutlama) ve Implementation (Uygulama) kavramları korkutucu mu geliyor. Paniğe gerek yok, hemen basit iki örnek düşünelim;

Diyelimki Circle (Çember) ve Square (Kare) gibi bir kaç alt sınıfı olan Shape (Şekil) adlı bir sınıfınız olsun. Bu sınıf hiyerarşisini renkleri de dahile decek şekilde genişletmek istiyorsunuz ve bu amaçla Red (Kırmızı) ve Blue mavi şekil alt sınıflarını oluşturdunuz. Fakat zaten iki şekil alt sınıfınız olduğu için MaviDaire, KırmızıKare gibi dört farklı kombinasyon hazırlamanız gerekecek.

Sınıf kombinasyonları geometrik olarak artacaktır.

Yeni şekil tür veya renkleri eklemek hiyerarşinizi eksponensiyel olarak büyütecektir. Örneğin bir üçgen eklemek istediğinizde her iki renk için de ayrı alt sınıflar oluşturmanız gerekecek. Ve daha sonra yeni bir renk eklemek isterseniz her şekil için bir tane olmak üzere üç alt sınıf daha eklemeniz gerekecek. Daha fazlasını ekledikçe bu durum daha da kötüye gidecek.

Çözüm

Bu sorunla karşılaşmamızın nedeni şekil alt sınıfını iki bağımsız boyutta genişletmeye çalışmamız. Bu sınıf türetme (class inheritance) yaparken maalesef karşınıza sık çıkacak bir sorundur.

Köprü deseni (Bridge pattern) bu kalıtsal türetme (inheritance) işini nesne bileşimi haline getirerek çözüyor. Bunun anlamı bu iki boyuttan birini ayrı bir sınıf haline getiriyor ve orijinal sınıf içerisinden buna referans veriyorsunuz, böylece orijinal sınıf diğer sınıfın bütün metod ve durumlarını içermek zorunda kalmıyor.

Sınıf hiyerarşinizin gereksiz büyümesini engellemek için birden fazla ilişkili sınıflara ayırabilirsiniz.

Bu yaklaşımı uygulayarak renkle ilgili kodları Kırmızı ve Mavi alt sınıfları içine koyabiliriz. Böylece şekil sınıfının sadece ilgili renk sınıfına bir referans tutması yeterli olur.

Uygulanabilirlik

Belirli bir işlevin çeşitli varyasyonlarını içeren monoloitik bir sınıfı parçalara bölmek ve yönetmek istediğinizde köprü (Bridge) tasarım desenini kulllanın.

Sınıf büyüdükçe nasıl çalıştığını anlamak daha zor olacak, gerektiğinde değişiklik yapmak daha fazla vakit alacaktır. Varyasyonların birinde yapılan bir değişiklik tüm sınıfta değişiklik yapmayı gerektirebilir. Böyle bir değişiklik genelde hataya açık veya farkedilmeyen kritik yan etkilere neden olabilir.

Köprü deseni monolotilik bir sınıfı bir kaç sınıf hiyerarşisine döndürmenize olanağ sağlar. Bunu yaptıktan sonra hiyerarşideki sınıfları diğerlerinden bağımsız olarak değiştirebilirsiniz. Bu yaklaşım kodun bakımını kolaylaştırırken mevcut kodda hatalara neden olma riskini de azaltır.

Bir sınıfı bağımsız birden fazla boyuta genişletecekseniz bu sınıfı kullanın.

Köprü tüm boyutlar için ayrı sınıflar oluşturmayı önerir. Orijinal sınıf tüm işi kendi yapmaktansa bu alt sınıflara yükler.

Çalışma anında uygulama yöntemlerini değiştirmek için köprü desenini kullanın.

Her ne kadar isteğe bağlı da olsa, köprü deseni abstraction (soyutlama) içerisindeki uygulanışı (implementation) değiştirme olanağı sağlar. Basitçe ilgili alana yeni bir değer atayarak bunu sağlayabilirsiniz.

Diğer tasarım desenleri/kalıpları ile ilişkisi

  • Köprü (Bridge) uygulamanın farklı bölümlerini birbirinden bağımsız olarak geliştirmeniz için henüz o bölümler kodlanmadan önce geliştirilir. Adaptör ise mevcut bir uygulamada kullanılarak birbiri ile uyumsuz sınıfların birlikte çalışmasını sağlar.
  • Bridge ile birlikte Abstract Factory kullanabilirsiniz. Köprünün (bridge) tanımladığı bazı soyutlamalar (abstraction) sadece belirli uygulanışlar (implementation) için geçerli olduğunda bu iş birliği kullanışlıdır. Bu durumda Abstract Factory bu ilişkileri kapsamına alır ve kompleks yapıyı istemci koddan gizlemiş olur.
  • Builder ve Bridge‘i birleştirebilirsiniz. Yönetici (director) sınıf soyutlama (abstraction) rolünü oynarken, farklı builder’lar uygulanışı (implementation) belirler.

Bridge Tasarım Deseni Kod Örnekleri

Örnek PHP Kodu

<?php

namespace RefactoringGuru\Bridge\Conceptual;

/**
 * The Abstraction defines the interface for the "control" part of the two class
 * hierarchies. It maintains a reference to an object of the Implementation
 * hierarchy and delegates all of the real work to this object.
 */
class Abstraction
{
    /**
     * @var Implementation
     */
    protected $implementation;

    public function __construct(Implementation $implementation)
    {
        $this->implementation = $implementation;
    }

    public function operation(): string
    {
        return "Abstraction: Base operation with:\n" .
            $this->implementation->operationImplementation();
    }
}

/**
 * You can extend the Abstraction without changing the Implementation classes.
 */
class ExtendedAbstraction extends Abstraction
{
    public function operation(): string
    {
        return "ExtendedAbstraction: Extended operation with:\n" .
            $this->implementation->operationImplementation();
    }
}

/**
 * The Implementation defines the interface for all implementation classes. It
 * doesn't have to match the Abstraction's interface. In fact, the two
 * interfaces can be entirely different. Typically the Implementation interface
 * provides only primitive operations, while the Abstraction defines higher-
 * level operations based on those primitives.
 */
interface Implementation
{
    public function operationImplementation(): string;
}

/**
 * Each Concrete Implementation corresponds to a specific platform and
 * implements the Implementation interface using that platform's API.
 */
class ConcreteImplementationA implements Implementation
{
    public function operationImplementation(): string
    {
        return "ConcreteImplementationA: Here's the result on the platform A.\n";
    }
}

class ConcreteImplementationB implements Implementation
{
    public function operationImplementation(): string
    {
        return "ConcreteImplementationB: Here's the result on the platform B.\n";
    }
}

/**
 * Except for the initialization phase, where an Abstraction object gets linked
 * with a specific Implementation object, the client code should only depend on
 * the Abstraction class. This way the client code can support any abstraction-
 * implementation combination.
 */
function clientCode(Abstraction $abstraction)
{
    // ...

    echo $abstraction->operation();

    // ...
}

/**
 * The client code should be able to work with any pre-configured abstraction-
 * implementation combination.
 */
$implementation = new ConcreteImplementationA();
$abstraction = new Abstraction($implementation);
clientCode($abstraction);

echo "\n";

$implementation = new ConcreteImplementationB();
$abstraction = new ExtendedAbstraction($implementation);
clientCode($abstraction);

Örnek Python Kodu

from __future__ import annotations
from abc import ABC, abstractmethod


class Abstraction:
    """
    The Abstraction defines the interface for the "control" part of the two
    class hierarchies. It maintains a reference to an object of the
    Implementation hierarchy and delegates all of the real work to this object.
    """

    def __init__(self, implementation: Implementation) -> None:
        self.implementation = implementation

    def operation(self) -> str:
        return (f"Abstraction: Base operation with:\n"
                f"{self.implementation.operation_implementation()}")


class ExtendedAbstraction(Abstraction):
    """
    You can extend the Abstraction without changing the Implementation classes.
    """

    def operation(self) -> str:
        return (f"ExtendedAbstraction: Extended operation with:\n"
                f"{self.implementation.operation_implementation()}")


class Implementation(ABC):
    """
    The Implementation defines the interface for all implementation classes. It
    doesn't have to match the Abstraction's interface. In fact, the two
    interfaces can be entirely different. Typically the Implementation interface
    provides only primitive operations, while the Abstraction defines higher-
    level operations based on those primitives.
    """

    @abstractmethod
    def operation_implementation(self) -> str:
        pass


"""
Each Concrete Implementation corresponds to a specific platform and implements
the Implementation interface using that platform's API.
"""


class ConcreteImplementationA(Implementation):
    def operation_implementation(self) -> str:
        return "ConcreteImplementationA: Here's the result on the platform A."


class ConcreteImplementationB(Implementation):
    def operation_implementation(self) -> str:
        return "ConcreteImplementationB: Here's the result on the platform B."


def client_code(abstraction: Abstraction) -> None:
    """
    Except for the initialization phase, where an Abstraction object gets linked
    with a specific Implementation object, the client code should only depend on
    the Abstraction class. This way the client code can support any abstraction-
    implementation combination.
    """

    # ...

    print(abstraction.operation(), end="")

    # ...


if __name__ == "__main__":
    """
    The client code should be able to work with any pre-configured abstraction-
    implementation combination.
    """

    implementation = ConcreteImplementationA()
    abstraction = Abstraction(implementation)
    client_code(abstraction)

    print("\n")

    implementation = ConcreteImplementationB()
    abstraction = ExtendedAbstraction(implementation)
    client_code(abstraction)

Diğer Tasarım Kalıpları/Design Patterns

Yaratımsal Kalıplar (Creational Patterns)

Yapısal Kalıplar (Structural Patterns)

Davranışsal Kalıplar (Behavioral Patterns)

Bir soru, öneri ya da yorumunuz mu var?

Evren Bal

Ben Evren BAL

1996'dan beri ‘Internet canlısıyım!’

Evren Bal Hakkında daha fazla bilgi.

Tanışmak isterseniz hemen sosyal medyadan iletişime geçebilirsiniz.

Bana Ulaşın

Bana Ulaşın

  • Bir sorunuz mu var?
  • Yazıda bir hata mı farkettiniz?
  • Sayfa ile ilgili bir öneriniz mi var?
  • Yazmamı önereceğiniz bir konu mu var?

Lütfen iletişim formunu kullanarak veya sosyal medya hesaplarımdan bana ulaşın.

Digital Ocean Logo

VPS sunucusu denemek ister misiniz?

Digital Ocean&apos;a referans bağlantımdan kayıt olarak yeni oluşturacağınız hesabınızı 60 gün geçerli 100$ kredi ile açabilirsiniz. Bu miktar yüksek performanslı VPS&apos;leri bile denemeniz için yeterlidir.

Yapacağınız onca kurulumun boşa gitmemesi için benim tavsiyem uzun vadede kullanabileceğiniz özellik ve maliyetlerde bir sunucu oluşturmanızdır.

Ücretsiz 100$ kredi bağlantısı

60 günlük denemeniz sonunda eğer devam etmek istemezseniz hiç bir ücret ödemeyeceksiniz. Hizmeti kullanmaya devam etmek isterseniz harcamanız 25$&apos;a ulaştığında benim hesabıma da 25$ kredi yüklenecektir.

Diğer bir deyişle, siz 100$ ücretsiz krediyi her halükarda kazanırken, daha sonra ücretli devam etmeye karar verirseniz ben de 25$ kredi kazanacağım.

Copyright © 2022 - Evren BAL