Memahami pola desain jembatan

24

Saya tidak mengerti pola desain "jembatan" sama sekali. Saya telah melalui berbagai situs web, tetapi mereka belum membantu.

Adakah yang bisa membantu saya dalam memahami hal ini?


sumber
2
Saya juga tidak mengerti. Looking forward untuk melihat jawaban :)
Violet Giraffe
Ada banyak situs web dan buku yang menggambarkan pola desain di luar sana. Saya rasa tidak ada gunanya mengulangi apa yang sudah ditulis, mungkin Anda bisa mengajukan pertanyaan tertentu. Butuh beberapa saat untuk memahaminya juga, terus beralih di antara berbagai sumber dan contoh.
Helena

Jawaban:

16

Dalam OOP kami menggunakan polimorfisme sehingga abstraksi dapat memiliki beberapa implementasi. Mari kita lihat contoh berikut:

//trains abstraction
public interface Train
{ 
    move();
}
public class MonoRail:Train
{
    public override move()
    {
        //use one track;
    }
}
public class Rail:Train
{
    public override move()
    {
        //use two tracks;
    }
}

Persyaratan baru diperkenalkan dan perlu membawa perspektif percepatan kereta, jadi ubah kode seperti di bawah ini.

    public interface Train
    { 
        void move();
    }
    public class MonoRail:Train
    {
        public override void move()
        {
            //use one track;
        }
    }
    public class ElectricMonoRail:MonoRail
    {
        public override void move()
        {
            //use electric engine on one track.
        }
    }
    public class DieselMonoRail: MonoRail
    {
        public override void move()
        {
            //use diesel engine on one track.
        }
    }
    public class Rail:Train
    {
        public override void move()
        {
            //use two tracks;
        }
    }
    public class ElectricRail:Rail
    {
        public override void move()
        {
            //use electric engine on two tracks.
        }
    }
    public class DieselRail: Rail
    {
        public override void move()
        {
            //use diesel engine on two tracks.
        }
    }

Kode di atas tidak dapat dipertahankan dan kurang dapat digunakan kembali (dengan asumsi kita dapat menggunakan kembali mekanisme akselerasi untuk platform trek yang sama). Kode berikut menerapkan pola jembatan dan memisahkan dua abstraksi yang berbeda, transportasi kereta api dan akselerasi .

public interface Train
{ 
    void move(Accelerable engine);
}
public interface Accelerable
{
    public void accelerate();
}
public class MonoRail:Train
{
    public override void move(Accelerable engine)
    {
        //use one track;
        engine.accelerate(); //engine is pluggable (runtime dynamic)
    }
}
public class Rail:Train
{
    public override void move(Accelerable engine)
    {
        //use two tracks;
        engine.accelerate(); //engine is pluggable (runtime dynamic)
    }
}
public class ElectricEngine:Accelerable{/*implementation code for accelerable*/}
public class DieselEngine:Accelerable{/*implementation code for accelerable*/}
Thurein
sumber
3
Contoh yang sangat bagus. Saya akan menambahkan dua sen saya: ini adalah contoh yang sangat baik untuk lebih memilih komposisi daripada warisan
zzfima
1
Untuk apa nilainya, saya pikir itu harus Monorailkarena itu bukan dua kata, itu kata tunggal (gabungan). MonoRail akan menjadi beberapa subclass dari Rail alih-alih jenis rail yang berbeda (seperti itu). Sama seperti kita tidak akan menggunakan SunShineatau CupCake, mereka akan SunshinedanCupcake
ErikE
Baris ini "dua abstraksi yang berbeda, transportasi kereta api dan akselerasi" membantu, menunjukkan apa yang dua hierarki dan apa yang kita coba selesaikan adalah untuk membuatnya berbeda secara independen.
wangdq
11

Sementara sebagian besar pola desain memiliki nama yang membantu, saya menemukan nama "Bridge" menjadi non-intuitif dalam hal apa yang dilakukannya.

Secara konseptual, Anda mendorong detail implementasi yang digunakan oleh hierarki kelas ke objek lain, biasanya dengan hierarki sendiri. Dengan melakukannya, Anda menghilangkan ketergantungan yang ketat pada detail implementasi tersebut dan memungkinkan detail implementasi tersebut berubah.

Dalam skala kecil, saya menyamakan ini dengan penggunaan pola strategi dalam cara Anda bisa memasukkan perilaku baru. Tetapi alih-alih hanya membungkus suatu algoritma seperti yang sering terlihat dalam suatu strategi, objek implementasi biasanya lebih dipenuhi fitur. Dan ketika Anda menerapkan konsep itu ke seluruh hierarki kelas, pola yang lebih besar menjadi Jembatan. (Sekali lagi, benci nama itu).

Ini bukan pola yang akan Anda gunakan setiap hari, tapi saya merasa terbantu ketika mengelola potensi ledakan kelas yang bisa terjadi ketika Anda memiliki (jelas) kebutuhan akan pewarisan berganda.

Ini adalah contoh dunia nyata:

Saya memiliki alat RAD yang memungkinkan Anda menjatuhkan dan mengonfigurasi kontrol pada permukaan desain, jadi saya memiliki model objek seperti ini:

Widget // base class with design surface plumbing
+ Top
+ Left
+ Width
+ Height
+ Name
+ SendToBack
+ BringToFront
+ OnPropertyEdit
+ OnSelect
+ Validate
+ ShowEditor
+ Paint
+ Etc

TextboxWidget : Widget // text box specific
+ Text
+ MaxLength
+ Font
+ ShowEditor // override base to show a property editor form specific to a Textbox
+ Paint // override to render a textbox onto the surface    
+ Etc

ListWidget : Widget // list specific
+ Items
+ SelectedItem
+ ShowEditor // override base to show a property editor form specific to a List
+ Paint // override to render a list onto the surface
+ Etc

Dan seterusnya, mungkin dengan selusin kontrol.

Tapi kemudian persyaratan baru ditambahkan untuk mendukung banyak tema (lihat-n-terasa). Katakanlah kita memiliki tema-tema berikut: Win32, WinCE, WinPPC, WinMo50, WinMo65. Setiap tema akan memiliki nilai atau implementasi yang berbeda untuk operasi terkait rendering seperti DefaultFont, DefaultBackColor, BorderWidth, DrawFrame, DrawScrollThumb, dll.

Saya bisa membuat model objek seperti ini:

Win32TextboxWidget : TextboxWidget

Win32ListWidget : ListWidget

dll, untuk satu tipe kontrol

WinCETextboxWidget : TextboxWidget

WinCEListWidget : ListWidget

dll., untuk setiap jenis kontrol lainnya (lagi)

Anda mendapatkan idenya — Anda mendapatkan ledakan kelas # widget kali # tema. Ini menyulitkan perancang RAD dengan membuatnya sadar akan setiap tema. Plus, menambahkan tema baru memaksa perancang RAD untuk dimodifikasi. Lebih lanjut, ada banyak implementasi bersama dalam suatu tema yang akan bagus untuk diwariskan, tetapi kontrol sudah mewarisi dari basis bersama ( Widget).

Jadi alih-alih yang saya lakukan adalah membuat hierarki objek terpisah yang mengimplementasikan tema. Setiap widget akan menyimpan referensi ke objek yang mengimplementasikan operasi rendering. Dalam banyak teks, kelas ini diberi akhiran dengan Impltapi saya menyimpang dari konvensi penamaan itu.

Jadi sekarang TextboxWidgetpenampilan saya seperti ini:

TextboxWidget : Widget // text box specific
+ Text
+ MaxLength
+ Font
+ ShowEditor
+ Painter // reference to the implementation of the widget rendering operations
+ Etc

Dan saya bisa membuat pelukis saya mewarisi basis tema khusus saya, yang tidak bisa saya lakukan sebelumnya:

Win32WidgetPainter
+ DefaultFont
+ DefaultFontSize
+ DefaultColors
+ DrawFrame
+ Etc

Win32TextboxPainter : Win32WidgetPainter

Win32ListPainter : Win32WidgetPainter

Salah satu hal yang menyenangkan adalah bahwa saya dapat secara dinamis memuat implementasi pada saat run-time, memungkinkan saya untuk menambahkan sebanyak mungkin tema yang saya inginkan tanpa mengubah perangkat lunak inti. Dengan kata lain, "implementasi saya dapat bervariasi terlepas dari abstraksi".

tcarvin
sumber
Saya tidak mengerti bagaimana ini seharusnya menjadi pola jembatan? Saat Anda menambahkan komponen baru ke hierarki Widget, Anda dipaksa untuk menambahkan Widget baru itu ke SEMUA Pelukis (Win32NewWidgetPainter, PPCNewWidgetPainter). Ini BUKAN dua hierarki yang tumbuh secara independen. Untuk pola bridge yang tepat, Anda tidak akan mensubclass kelas PlatformWidgetPainter untuk setiap widget, melainkan menerima widget "draw deskriptor".
Mazyod
Terima kasih atas umpan baliknya, Anda benar. Bertahun-tahun yang lalu saya memposting ini, dan meninjaunya sekarang saya akan mengatakan itu menggambarkan jembatan dengan baik sampai bit terakhir di mana saya berasal Win32TextboxPainterdan Win32ListPainter dari Win32WidgetPainter. Anda dapat memiliki pohon warisan di sisi pelaksanaan, tetapi harus lebih generik (mungkin StaticStyleControlPainter, EditStyleControlPainterdan ButtonStyleControlPainter) dengan operasi primitif yang dibutuhkan diganti sesuai kebutuhan. Ini lebih dekat dengan kode asli yang saya mendasarkan contoh.
tcarvin
3

Jembatan bermaksud memisahkan satu abstraksi dari implementasi konkretnya , sehingga keduanya dapat berbeda secara independen:

  • sempurnakan abstraksi dengan subclassing
  • memberikan implementasi yang berbeda, juga dengan mensubklasifikasi, tanpa harus mengetahui abstraksi maupun perbaikannya.
  • jika perlu, pilih saat runtime yang merupakan implementasi paling cocok .

Jembatan mencapai ini dengan menggunakan komposisi:

  • abstraksi merujuk (referensi atau pointer) ke objek implementasi
  • abstraksi dan penyempurnaannya hanya diketahui antarmuka implementasinya

masukkan deskripsi gambar di sini

Keterangan tambahan tentang kebingungan yang sering terjadi

Pola ini sangat mirip dengan pola adaptor: abstraksi menawarkan antarmuka yang berbeda untuk implementasi dan menggunakan komposisi untuk melakukannya. Tapi:

Perbedaan utama antara pola-pola ini terletak pada niat mereka
- Gamma & al, dalam " Pola desain, elemen perangkat lunak OO yang dapat digunakan kembali " , 1995

Dalam buku mani yang luar biasa tentang pola desain ini, penulis juga mengamati bahwa:

  • Adaptor sering digunakan ketika ketidakcocokan ditemukan dan kopling tidak terduga
  • jembatan digunakan sejak awal desain, ketika diharapkan bahwa kelas akan berkembang secara mandiri.
Christophe
sumber