Köprü Deseni ne zaman kullanılır? Adaptör modelinden farkı nedir?


154

Köprü Kalıbı'nı gerçek dünya uygulamasında kullanan biri oldu mu? Öyleyse, nasıl kullandınız? Benim mi, yoksa karışıma biraz bağımlılık enjeksiyonu ile sadece Adaptör Deseni mi? Gerçekten kendi modelini hak ediyor mu?


Lütfen bu soruya farklı bir cevap almayı düşünün. Şu anda kabul edilen cevap yanlış ve yararsızdır. Daha yeni cevaplar çok daha üstündür.
jaco0646

GoF kitap doğrudan bu soruyu cevaplar.
jaco0646

Yanıtlar:


76

Köprü deseninin klasik bir örneği, bir UI ortamındaki şekillerin tanımında kullanılır (bkz. Köprü modeli Wikipedia girişi ). Köprü desen bir olan kompozit ait Şablon ve Strateji desenleri.

Köprü desenindeki Adaptör deseninin bazı yönleri ortak bir görünümdür. Ancak, bu makaleden alıntı yapmak için :

İlk bakışta, Köprü deseni, bir sınıfın bir tür arabirimi diğerine dönüştürmek için kullanıldığı Adaptör desenine çok benziyor. Ancak, Bağdaştırıcı modelinin amacı, bir veya daha fazla sınıfın arabiriminin belirli bir sınıfla aynı görünmesini sağlamaktır. Bridge deseni, bir sınıfın arabirimini uygulamasından ayırmak üzere tasarlanmıştır, böylece istemci kodunu değiştirmeden uygulamayı değiştirebilir veya değiştirebilirsiniz.


1
Bridge'in Şablon veya Strateji ile ilgisi yoktur. Köprü yapısal bir kalıptır. Şablon ve Strateji davranış kalıplarıdır.
jaco0646

249

Federico ve John'un cevaplarının bir kombinasyonu var .

Ne zaman:

                   ----Shape---
                  /            \
         Rectangle              Circle
        /         \            /      \
BlueRectangle  RedRectangle BlueCircle RedCircle

Refactor:

          ----Shape---                        Color
         /            \                       /   \
Rectangle(Color)   Circle(Color)           Blue   Red

6
Neden renkler için miras alıyorsunuz?
vainolo

10
@vainolo Çünkü Renk bir arayüz ve Mavi, Kırmızı beton renkler
Weltschmerz

3
Bu sadece yeniden düzenleme. Köprü deseni amacı: "İkisinin bağımsız olarak değişebilmesi için bir soyutlamanın uygulanmasından ayrıştırılması." Soyutlama nerede ve uygulama nerede?
clapas

1
Dikdörtgen (Renk) BlueRectangle öğesinden daha soyut değil mi?
Anton Shchastnyi

2
@clapas, Soyutlama "Shape.color" özelliğidir, bu nedenle Red sınıfı ve Blue sınıfı uygulama ve Color arayüzü ise köprüdür.
reco

230

Köprü deseni eski tavsiyenin bir uygulamasıdır, "miras yerine kompozisyonu tercih et". Farklı zamanları birbirleriyle dikey şekilde alt sınıflara ayırmanız gerektiğinde kullanışlı olur. Diyelim ki renkli şekillerden oluşan bir hiyerarşi uygulamalısınız. Shape'yi Rectangle ve Circle ile alt sınıflara ayırmazsınız ve daha sonra Rectangle'ı RedRectangle, BlueRectangle ve GreenRectangle ile alt sınıfla ve aynı Circle için yapmazsınız, değil mi? Her Şekli söylemek tercih ediyorum sahiptir Renk ve renk hiyerarşisi uygulamak ve bu Köprüsü Formasyonu oluştu. Bir "renk hiyerarşisi" uygulamam ama fikri anladınız ...


1
Bu açıklamanın grafiksel bir gösterimi için aşağıdaki Anton Shchastnyi diyagramına da bakınız.
NomadeNumerique

2
Bir rengin bir uygulama hiyerarşisi için iyi bir örnek olduğunu düşünmüyorum, oldukça kafa karıştırıcı. Uygulamanın platforma bağlı olduğu GoF tarafından "Tasarım desenleri" nde Köprü modeline iyi bir örnek vardır: IBM'in PM, UNIX's X vb.
clapas

216

Ne zaman:

        A
     /     \
    Aa      Ab
   / \     /  \
 Aa1 Aa2  Ab1 Ab2

Refactor:

     A         N
  /     \     / \
Aa(N) Ab(N)  1   2

3
Ben desenlere çok pragmatik bir yaklaşım olduğunu düşünüyorum: 1) düşük düz-ileri tasarımı açıklamak 2) daha iyi bir faktör için refactor tasarım / kod
Alexey

1
Köprü tasarım desenini açıklamak için matematik kavramını kullanın. Çok ilginç.
Jian Huang

1
Bu sadece yeniden düzenleme. Köprü deseni amacı: "İkisinin bağımsız olarak değişebilmesi için bir soyutlamanın uygulanmasından ayrıştırılması." Soyutlama nerede ve uygulama nerede?
clapas

John güzel bir blog yazısı koyar . Üst düzey genel bakış için iyi bir okuma olarak bulundu.
Vaibhav Bhalla

29

Bağdaştırıcı ve Köprü kesinlikle birbiriyle ilişkilidir ve ayrım çok incedir. Bu kalıplardan birini kullandıklarını düşünen bazı insanlar aslında diğer kalıpları kullanıyor olabilirler.

Gördüğüm açıklama, zaten var olan bazı uyumsuz sınıfların arayüzlerini birleştirmeye çalıştığınızda Bağdaştırıcının kullanıldığı . Bağdaştırıcı, eski olarak kabul edilebilecek uygulamalara bir tür çevirmen işlevi görür .

Köprü deseni ise yeşil alan olması muhtemel kodlar için kullanılır. Bridge'i, değişmesi gereken bir uygulama için soyut bir arabirim sağlayacak şekilde tasarlıyorsunuz, ancak bu uygulama sınıflarının arabirimini de tanımlıyorsunuz.

Aygıt sürücüleri, Bridge tarafından sıkça atıfta bulunulan bir örnektir, ancak aygıt satıcıları için arabirim spesifikasyonunu tanımlarsanız bunun bir Köprü olduğunu söyleyebilirim, ancak mevcut aygıt sürücülerini alıyorsanız ve birleşik bir arayüz sağlar.

Yani kod olarak, iki örüntü birbirine çok benzer. İş açısından farklılar.

Ayrıca bkz. Http://c2.com/cgi/wiki?BridgePattern


Hey Bill. Aygıt sürücülerinde neden Bridge desenini kullandığımızı anlamıyorum. Demek istediğim, uygulamayı (okuma, yazma, arama vb.) Polimorfizmle doğru sınıfa kolayca delege edebiliriz değil mi? Ya da bir Ziyaretçi ile? Neden Bridge olmalı? Şimdiden teşekkürler.
stdout

1
@zgulser, evet, polimorfizm kullanıyorsunuz. Bridge modeli, uygulamayı soyutlamadan ayırmak için bir tür alt sınıf kullanımını açıklar.
Bill Karwin

Shape uygulamasını (yani, Dikdörtgen) gün soyutlamadan ayırmak istediniz Renk soyutlama değil mi? Ve inanıyorum ki bunu yapmanın çeşitli yolları var ve Bridge bunlardan sadece biri.
stdout

Evet, alt sınıflamanın başka kullanımları vardır. Alt sınıfları kullanmanın bu özel yolu onu Köprü deseni yapan şeydir.
Bill Karwin

Demek istediğim ayrıştırma, soyut Shape arayüzünden somut bir Dikdörtgen uygulamasına kadar. Böylece, somut nesne gerçekten bir Shape alt sınıfı olmasına rağmen, "Shape" tipinde bir nesneye ihtiyaç duyan bir kod yazabilirsiniz.
Bill Karwin

27

Deneyimlerime göre, Bridge oldukça sık tekrar eden bir modeldir, çünkü etki alanında iki dikey boyut olduğunda çözüm budur . Örneğin şekiller ve çizim yöntemleri, davranışlar ve platformlar, dosya formatları ve serileştiriciler vb.

Ve bir tavsiye: tasarım kalıplarını daima uygulama perspektifinden değil , kavramsal perspektiften düşünün . Doğru bakış açısından, Bridge, Adaptör ile karıştırılamaz, çünkü farklı bir sorunu çözerler ve kompozisyon, kendi uğruna değil, dikey endişelerin ayrı ayrı ele alınmasına izin verdiği için mirastan daha üstündür.


22

Amacı Köprüsü ve Adaptörü farklıdır ve ayrı ayrı her iki desenleri gerekir.

Köprü deseni:

  1. Yapısal bir kalıptır
  2. Derleme zamanında soyutlama ve uygulama sınırlı değildir
  3. Soyutlama ve uygulama - her ikisi de müşteri üzerinde herhangi bir etki yaratmadan değişiklik gösterebilir
  4. Kompozisyonu kalıtım üzerine kullanır.

Bridge desenini şu durumlarda kullanın:

  1. Uygulamanın çalışma zamanı bağlayıcılığını istiyorsunuz,
  2. Birleştirilmiş arayüz ve çok sayıda uygulamadan kaynaklanan sınıfların çoğalması var,
  3. Bir uygulamayı birden çok nesne arasında paylaşmak istiyorsunuz,
  4. Dik sınıf hiyerarşilerini eşlemeniz gerekir.

@ John Sonmez yanıtı, sınıf hiyerarşisini azaltmada köprü modelinin etkinliğini açıkça göstermektedir.

Kod örneği ile köprü deseni hakkında daha iyi bilgi edinmek için aşağıdaki dokümantasyon bağlantısına bakabilirsiniz.

Adaptör deseni :

  1. Bu çalışma birlikte ile iki ilgisiz arayüzleri verir muhtemelen aynı rolü oynayan, farklı nesneler aracılığıyla.
  2. Orijinal arayüzü değiştirir.

Temel farklılıklar:

  1. Adaptör işleri tasarlandıktan sonra çalışır hale getirir; Bridge onları daha önce çalıştırır.
  2. Köprü , soyutlamanın ve uygulamanın bağımsız olarak değişmesini sağlamak için ön tarafta tasarlanmıştır . Bağdaştırıcı olmayan sınıfların birlikte çalışması için adaptör sonradan takılır.
  3. Amaç: Bağdaştırıcı , ilişkisiz iki arabirimin birlikte çalışmasına izin verir. Köprü , Soyutlama ve uygulamanın bağımsız olarak değişmesine izin verir.

UML diyagramı ve çalışma kodu ile ilgili SE sorusu:

Köprü deseni ve Adaptör deseni arasındaki fark

Yararlı makaleler:

kaynak yapımı köprü desen makale

kaynak yapımı adaptör deseni makalesi

journaldev köprü desen makalesi

DÜZENLE:

Köprü Deseni gerçek dünya örneği (meta.stackoverflow.com önerisine göre, belgeler yayınlanacağı için bu yazıya dahil edilen dokümantasyon sitesi örneği)

Köprü deseni, soyutlamayı uygulamadan ayırır, böylece her ikisi de bağımsız olarak değişebilir. Kalıtımdan çok kompozisyon ile başarılmıştır.

Wikipedia'dan köprü deseni UML'si:

Wikipedia'dan köprü deseni UML'si

Bu modelde dört bileşene sahipsiniz.

Abstraction: Bir arayüz tanımlar

RefinedAbstraction: Soyutlamayı uygular:

Implementor: Uygulama için bir arayüz tanımlar

ConcreteImplementor: Implementor arabirimini uygular.

The crux of Bridge pattern :Kompozisyon kullanan (ve kalıtım olmadan) iki dikey sınıf hiyerarşisi. Soyutlama hiyerarşisi ve Uygulama hiyerarşisi bağımsız olarak değişebilir. Uygulama asla Soyutlama anlamına gelmez. Soyutlama, üye olarak Uygulama arabirimi içerir (kompozisyon aracılığıyla). Bu kompozisyon bir miras hiyerarşisi seviyesini daha azaltır.

Gerçek kelime Kullanım örneği:

Farklı araçların hem manuel hem de otomatik vites sisteminin sürümlerine sahip olmasını sağlayın.

Örnek kod:

/* Implementor interface*/
interface Gear{
    void handleGear();
}

/* Concrete Implementor - 1 */
class ManualGear implements Gear{
    public void handleGear(){
        System.out.println("Manual gear");
    }
}
/* Concrete Implementor - 2 */
class AutoGear implements Gear{
    public void handleGear(){
        System.out.println("Auto gear");
    }
}
/* Abstraction (abstract class) */
abstract class Vehicle {
    Gear gear;
    public Vehicle(Gear gear){
        this.gear = gear;
    }
    abstract void addGear();
}
/* RefinedAbstraction - 1*/
class Car extends Vehicle{
    public Car(Gear gear){
        super(gear);
        // initialize various other Car components to make the car
    }
    public void addGear(){
        System.out.print("Car handles ");
        gear.handleGear();
    }
}
/* RefinedAbstraction - 2 */
class Truck extends Vehicle{
    public Truck(Gear gear){
        super(gear);
        // initialize various other Truck components to make the car
    }
    public void addGear(){
        System.out.print("Truck handles " );
        gear.handleGear();
    }
}
/* Client program */
public class BridgeDemo {    
    public static void main(String args[]){
        Gear gear = new ManualGear();
        Vehicle vehicle = new Car(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Car(gear);
        vehicle.addGear();

        gear = new ManualGear();
        vehicle = new Truck(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Truck(gear);
        vehicle.addGear();
    }
}

çıktı:

Car handles Manual gear
Car handles Auto gear
Truck handles Manual gear
Truck handles Auto gear

Açıklama:

  1. Vehicle bir soyutlamadır.
  2. Carve Truckiki somut uygulamasıdır Vehicle.
  3. Vehiclesoyut bir yöntem tanımlar: addGear().
  4. Gear implementor arayüzü
  5. ManualGearve AutoGeariki uygulamasıdır Gear
  6. Vehicleimplementorarabirimi uygulamak yerine arabirim içerir . Compositonarayüzünün örneği bu modelin temelidir: Soyutlamanın ve uygulamanın bağımsız olarak değişmesine izin verir.
  7. Carve Trucksoyutlama için uygulamayı (yeniden tanımlanmış soyutlama) tanımlayın addGear():: İçerir Gear- Ya ManualyaAuto

Köprü deseni için kullanım örnekleri :

  1. Soyutlama ve Uygulama birbirinden bağımsız olarak değişebilir ve derleme zamanında bağlı değildir
  2. Ortogonal hiyerarşileri eşleyin - Biri Soyutlama ve diğeri Uygulama için .

"Bağdaştırıcı işleri tasarlandıktan sonra çalışır hale getirir; Bridge onları daha önce çalıştırır." Takılabilir Adaptöre bakmak isteyebilirsiniz. Bu, GoF tarafından Tasarım Kalıpları kitaplarının "Bağdaştırıcı" bölümünde açıklanan bir Bağdaştırıcı varyasyonudur. Amaç henüz mevcut olmayan sınıflar için bir arayüz oluşturmaktır. Takılabilir bir adaptör bir köprü değildir, bu yüzden ilk noktanın geçerli olduğunu düşünmüyorum.
c1moore

Manuel ve otomatik vites kamyon ve otomobil için farklı uygulamalar gerektirse de
andigor

9

Köprü desenini işte kullandım. Ben genellikle PIMPL deyim (uygulama işaretçisi) denir C ++, programlayın. Şöyle görünüyor:

class A
{
public: 
  void foo()
  {
    pImpl->foo();
  }
private:
  Aimpl *pImpl;
};

class Aimpl
{
public:
  void foo();
  void bar();
};  

Bu örnekte class Aarabirimi ve class Aimpluygulamayı içerir.

Bu örüntü için bir kullanım, uygulama sınıfının sadece bazı kamu üyelerini ortaya çıkarmaktır, diğerlerini değil. Örnekte sadece Aimpl::foo()genel arayüzü üzerinden çağrılabilir A, ancakAimpl::bar()

Başka bir avantaj, Aimplkullanıcıları tarafından dahil edilmesi gerekmeyen ayrı bir başlık dosyasında tanımlayabilmenizdir A. Yapmanız gereken tek şey, daha Aimplönce Atanımlanmış bir ileri bildirim kullanmak ve başvuruda bulunan tüm üye işlevlerin tanımlarını pImpl.cpp dosyasına taşımaktır . Bu, Aimplüstbilgiyi gizli tutma ve derleme süresini azaltma olanağı sağlar.


2
Bu kalıbı kullanırsanız, AImpl'nin bir başlığa bile ihtiyacı yoktur. Ben sadece A sınıfı için uygulama dosyasına satır içi koymak
1800 BİLGİ

Uygulayıcınız özeldir. Bununla ilgili yeni bir sorum var, bkz. Stackoverflow.com/questions/17680762/…
Roland

7

Koda şekil örneği koymak için:

#include<iostream>
#include<string>
#include<cstdlib>

using namespace std;

class IColor
{
public:
    virtual string Color() = 0;
};

class RedColor: public IColor
{
public:
    string Color()
    {
        return "of Red Color";
    }
};

class BlueColor: public IColor
{
public:
    string Color()
    {
        return "of Blue Color";
    }
};


class IShape
{
public:
virtual string Draw() = 0;
};

class Circle: public IShape
{
        IColor* impl;
    public:
        Circle(IColor *obj):impl(obj){}
        string Draw()
        {
            return "Drawn a Circle "+ impl->Color();
        }
};

class Square: public IShape
{
        IColor* impl;
    public:
        Square(IColor *obj):impl(obj){}
        string Draw()
        {
        return "Drawn a Square "+ impl->Color();;
        }
};

int main()
{
IColor* red = new RedColor();
IColor* blue = new BlueColor();

IShape* sq = new Square(red);
IShape* cr = new Circle(blue);

cout<<"\n"<<sq->Draw();
cout<<"\n"<<cr->Draw();

delete red;
delete blue;
return 1;
}

Çıktı:

Drawn a Square of Red Color
Drawn a Circle of Blue Color

Permütasyonlar nedeniyle alt sınıfların patlamasına yol açmadan sisteme yeni renklerin ve şekillerin eklenebilme kolaylığına dikkat edin.


0

benim için bunu arayüzleri değiştirebileceğiniz bir mekanizma olarak düşünüyorum. Gerçek dünyada birden fazla arabirim kullanabilen bir sınıfa sahip olabilirsiniz, Bridge değiştirmenize izin verir.


0

Muhasebe, sözleşme, talepler gibi farklı görevleri yöneten bir iş akışı uygulaması geliştirdiğiniz bir sigorta şirketi için çalışıyorsunuz. Bu soyutlama. Uygulama tarafında, farklı kaynaklardan görevler oluşturabilmeniz gerekir: e-posta, faks, e-mesajlaşma.

Tasarımınıza şu sınıflarla başlıyorsunuz:

public class Task {...}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}

Şimdi, her kaynak belirli bir şekilde ele alınması gerektiğinden, her görev türünü uzmanlaştırmaya karar verdiniz:

public class EmailAccountingTask : AccountingTask {...}
public class FaxAccountingTask : AccountingTask {...}
public class EmessagingAccountingTask : AccountingTask {...}

public class EmailContractTask : ContractTask {...}
public class FaxContractTask : ContractTask {...}
public class EmessagingContractTask : ContractTask {...}

public class EmailClaimTask : ClaimTask {...}
public class FaxClaimTask : ClaimTask {...}
public class EmessagingClaimTask : ClaimTask {...}

13 ders ile sonuçlanırsınız. Görev türü veya kaynak türü eklemek zorlaşır. Köprü kalıbını kullanmak, görevi (soyutlama) kaynaktan (bir uygulama sorunu olan) ayırarak bakımı daha kolay bir şey üretir:

// Source
public class Source {
   public string GetSender();
   public string GetMessage();
   public string GetContractReference();
   (...)
}

public class EmailSource : Source {...}
public class FaxSource : Source {...}
public class EmessagingSource : Source {...}

// Task
public class Task {
   public Task(Source source);
   (...)
}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}

Görev türü veya kaynak eklemek artık çok daha kolay.

Not: Çoğu geliştirici bu sorunu çözmek için 13 sınıf hiyerarşisini önceden oluşturmaz. Ancak, gerçek hayatta, kaynak ve görev türlerinin sayısını önceden bilmiyor olabilirsiniz; yalnızca bir kaynağınız ve iki görev türünüz varsa, muhtemelen Görevi Kaynaktan ayırmazsınız. Ardından, yeni kaynaklar ve görev türleri eklendikçe genel karmaşıklık artar. Bir noktada, yeniden düzenleyecek ve çoğu zaman köprü benzeri bir çözüm elde edeceksiniz.


-5
Bridge design pattern we can easily understand helping of service and dao layer.

Dao layer -> create common interface for dao layer ->
public interface Dao<T>{
void save(T t);
}
public class AccountDao<Account> implement Dao<Account>{
public void save(Account){
}
}
public LoginDao<Login> implement Dao<Login>{
public void save(Login){
}
}
Service Layer ->
1) interface
public interface BasicService<T>{
    void save(T t);
}
concrete  implementation of service -
Account service -
public class AccountService<Account> implement BasicService<Account>{
 private Dao<Account> accountDao;
 public AccountService(AccountDao dao){
   this.accountDao=dao;
   }
public void save(Account){
   accountDao.save(Account);
 }
}
login service- 
public class LoginService<Login> implement BasicService<Login>{
 private Dao<Login> loginDao;
 public AccountService(LoginDao dao){
   this.loginDao=dao;
   }
public void save(Login){
   loginDao.save(login);
 }
}

public class BridgePattenDemo{
public static void main(String[] str){
BasicService<Account> aService=new AccountService(new AccountDao<Account>());
Account ac=new Account();
aService.save(ac);
}
}
}

5
Aşağı düştüm, çünkü bunun kıvrımlı, kötü biçimlendirilmiş bir cevap olduğunu hissediyorum.
Zimano

1
Tamamen katılıyorum, kod girintisine ve netliğine çok az dikkat etmeden bu sitede nasıl cevap gönderebilirsiniz
Massimiliano Kraus
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.