ทำความเข้าใจกับรูปแบบการออกแบบสะพาน


24

ฉันไม่เข้าใจรูปแบบการออกแบบ "สะพาน" เลย ฉันได้ผ่านเว็บไซต์ต่าง ๆ แต่พวกเขาไม่ได้ช่วย

ใครช่วยฉันในการทำความเข้าใจสิ่งนี้


2
ฉันก็ไม่เข้าใจเหมือนกัน รอคอยที่จะได้เห็นคำตอบ :)
Violet Giraffe

มีเว็บไซต์และหนังสือมากมายที่อธิบายรูปแบบการออกแบบ ฉันไม่คิดว่าจะมีค่าในการทำซ้ำสิ่งที่เขียนไปแล้วบางทีคุณอาจถามคำถามเฉพาะ ฉันใช้เวลาสักครู่เพื่อทำความเข้าใจเช่นกันการสลับระหว่างแหล่งข้อมูลและตัวอย่าง
เฮเลนา

คำตอบ:


16

ใน OOP เราใช้ polymorphism เพื่อให้นามธรรมสามารถมีการใช้งานที่หลากหลาย ลองดูตัวอย่างต่อไปนี้:

//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;
    }
}

ข้อกำหนดใหม่ได้รับการแนะนำและต้องการนำมาใช้ในมุมมองการเร่งความเร็วของรถไฟดังนั้นให้เปลี่ยนรหัสด้านล่าง

    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.
        }
    }

รหัสข้างต้นไม่สามารถบำรุงรักษาได้และขาดการนำกลับมาใช้ใหม่ (สมมติว่าเราสามารถใช้กลไกการเร่งความเร็วสำหรับแพลตฟอร์มแทร็กเดียวกัน) รหัสต่อไปนี้ใช้รูปแบบสะพานและแยกทั้งสองแนวคิดที่แตกต่างกัน, การขนส่งทางรถไฟและการเร่งความเร็ว

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*/}

3
ตัวอย่างที่ดีมาก ฉันจะเพิ่มสองเซ็นต์ของฉัน: นี่เป็นตัวอย่างที่ดีมากของการเลือกองค์ประกอบมากกว่ามรดก
zzfima

1
สำหรับสิ่งที่คุ้มค่าฉันคิดว่าควรเป็นMonorailเพราะไม่ใช่สองคำจริงๆเป็นคำเดียว (คำประสม) MonoRail จะเป็น subclass บางส่วนของ Rail แทนที่จะเป็น Rail แบบอื่น (ซึ่งก็คือ) เช่นเดียวกับที่เราจะไม่ใช้SunShineหรือCupCakeพวกเขาจะเป็นSunshineและCupcake
ErikE

บรรทัดนี้ "ทั้งสอง abstractions ต่าง ๆ รถไฟขนส่งและการเร่งความเร็ว" ช่วยชี้ให้เห็นว่าอะไรคือลำดับชั้นสองและสิ่งที่เราพยายามที่จะแก้ปัญหาคือการทำให้พวกเขาแตกต่างกันอย่างอิสระ
wangdq

11

ในขณะที่รูปแบบการออกแบบส่วนใหญ่มีชื่อที่มีประโยชน์ แต่ฉันพบว่าชื่อ "Bridge" นั้นไม่ง่ายต่อการใช้งาน

แนวคิดคุณส่งรายละเอียดการใช้งานที่ใช้โดยลำดับชั้นของคลาสไปยังวัตถุอื่นโดยทั่วไปจะมีลำดับชั้นของตัวเอง ด้วยการทำเช่นนี้คุณกำลังลบการพึ่งพารายละเอียดการใช้งานเหล่านั้นอย่างแน่นหนาและอนุญาตให้รายละเอียดของการปรับใช้นั้นเปลี่ยนแปลง

ในระดับน้อยฉันเปรียบสิ่งนี้กับการใช้รูปแบบกลยุทธ์ในวิธีที่คุณสามารถเสียบพฤติกรรมใหม่ แต่แทนที่จะห่อหุ้มอัลกอริทึมที่มักจะเห็นในกลยุทธ์วัตถุการใช้งานมักจะเต็มไปด้วยคุณสมบัติ และเมื่อคุณใช้แนวคิดกับลำดับชั้นของคลาสทั้งหมดรูปแบบที่ใหญ่กว่าจะกลายเป็นบริดจ์ (เกลียดชื่ออีกครั้ง)

ไม่ใช่รูปแบบที่คุณจะใช้ทุกวัน แต่ฉันพบว่ามีประโยชน์เมื่อจัดการการระเบิดของชั้นเรียนที่อาจเกิดขึ้นเมื่อคุณมีความต้องการ (ชัดเจน) สำหรับมรดกหลาย ๆ อย่าง

นี่คือตัวอย่างจริงของโลก:

ฉันมีเครื่องมือ RAD ที่ให้คุณดรอปและกำหนดค่าการควบคุมบนพื้นผิวการออกแบบดังนั้นฉันจึงมีโมเดลวัตถุเช่นนี้:

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

และบางทีอาจมีการควบคุมเป็นโหล

แต่จะมีการเพิ่มข้อกำหนดใหม่เพื่อรองรับหลายธีม (look-n-feel) สมมติว่าเรามีรูปแบบต่อไปนี้: Win32, WinCE, WinPPC, WinMo50,WinMo65 , แต่ละธีมจะมีค่าหรือการใช้งานที่แตกต่างกันสำหรับการดำเนินการที่เกี่ยวข้องกับการเรนเดอร์เช่น DefaultFont, DefaultBackColor, BorderWidth, DrawFrame, DrawScrollThumb เป็นต้น

ฉันสามารถสร้างแบบจำลองวัตถุเช่นนี้:

Win32TextboxWidget : TextboxWidget

Win32ListWidget : ListWidget

ฯลฯ สำหรับหนึ่งประเภทการควบคุม

WinCETextboxWidget : TextboxWidget

WinCEListWidget : ListWidget

ฯลฯ สำหรับประเภทการควบคุมซึ่งกันและกัน (อีกครั้ง)

คุณได้รับแนวคิด - คุณได้รับการระเบิดในระดับ # จำนวนวิดเจ็ตคูณด้วย # ของธีม สิ่งนี้ทำให้ผู้ออกแบบ RAD ยุ่งยากขึ้นโดยทำให้ตระหนักถึงทุกธีม นอกจากนี้การเพิ่มธีมใหม่จะบังคับให้ RAD ดีไซเนอร์ต้องถูกแก้ไข นอกจากนี้ยังมีการใช้งานร่วมกันจำนวนมากภายในชุดรูปแบบที่จะเป็นการดีที่จะสืบทอด แต่ตัวควบคุมกำลังสืบทอดจากฐานร่วม ( Widget)

ดังนั้นสิ่งที่ฉันทำคือสร้างลำดับชั้นของวัตถุแยกต่างหากที่ใช้ธีม แต่ละวิดเจ็ตจะมีการอ้างอิงไปยังวัตถุที่ใช้การดำเนินการการแสดงผล ในหลายตำราชั้นนี้จะต่อท้ายด้วยImplแต่ฉันเบี่ยงเบนไปจากแบบแผนการตั้งชื่อนั้น

ดังนั้นตอนนี้ฉันTextboxWidgetดูเหมือนว่านี้:

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

และฉันสามารถให้ช่างทาสีหลายคนสืบทอดฐานเฉพาะธีมของฉันซึ่งฉันไม่สามารถทำได้มาก่อน:

Win32WidgetPainter
+ DefaultFont
+ DefaultFontSize
+ DefaultColors
+ DrawFrame
+ Etc

Win32TextboxPainter : Win32WidgetPainter

Win32ListPainter : Win32WidgetPainter

หนึ่งในสิ่งที่ดีคือฉันสามารถโหลดแอพพลิเคชั่นในขณะใช้งานได้แบบไดนามิกทำให้ฉันสามารถเพิ่มธีมได้มากเท่าที่ฉันต้องการโดยไม่ต้องเปลี่ยนซอฟต์แวร์หลัก กล่าวอีกนัยหนึ่ง "การใช้งานของฉันอาจแตกต่างกันไปตามนามธรรม"


ฉันไม่เข้าใจว่านี่เป็นรูปแบบสะพานอย่างไร เมื่อคุณเพิ่มส่วนประกอบใหม่ในลำดับชั้นของ Widget คุณจะถูกบังคับให้เพิ่ม Widget ใหม่ให้กับจิตรกรทั้งหมด (Win32NewWidgetPainter, PPCNewWidgetPainter) นี่ไม่ใช่ลำดับชั้นที่เพิ่มขึ้นอย่างอิสระ สำหรับรูปแบบบริดจ์ที่เหมาะสมคุณจะไม่คลาสย่อย PlatformWidgetPainter สำหรับแต่ละวิดเจ็ต แต่ให้รับ Widget "ตัวอธิบายการวาด"
Mazyod

ขอบคุณสำหรับคำติชมคุณพูดถูก มันเป็นปีที่ผ่านมาที่ผมโพสต์นี้และตรวจสอบได้ในขณะนี้ผมจะบอกว่ามันอธิบายสะพานดีจนกว่าบิตสุดท้ายที่ผมได้รับมาWin32TextboxPainterและจากWin32ListPainter Win32WidgetPainterคุณสามารถมีต้นไม้มรดกในด้านการดำเนินการ แต่มันควรจะเป็นทั่วไปมากขึ้น (บางทีStaticStyleControlPainter, EditStyleControlPainterและButtonStyleControlPainter) มีการดำเนินงานใด ๆ ที่จำเป็นดั้งเดิมแทนที่ตามความจำเป็น นี่ใกล้กับรหัสจริงที่ฉันยกมาเป็นตัวอย่าง
tcarvin

3

สะพานมีความตั้งใจที่จะแยกสิ่งที่เป็นนามธรรมออกจากการดำเนินการที่เป็นรูปธรรมเพื่อให้ทั้งสองสามารถเปลี่ยนแปลงได้อย่างอิสระ:

  • ขัดเกลาสิ่งที่เป็นนามธรรมด้วย subclassing
  • ให้การใช้งานที่แตกต่างกันโดย subclassing โดยไม่ต้องรู้ว่าสิ่งที่เป็นนามธรรมหรือการปรับแต่ง
  • ถ้าต้องการเลือกที่รันไทม์ซึ่งเป็นที่เหมาะสมที่สุดในการดำเนินงาน

สะพานบรรลุเป้าหมายนี้โดยใช้การประพันธ์:

  • สิ่งที่เป็นนามธรรมหมายถึง (การอ้างอิงหรือตัวชี้) ไปยังวัตถุการดำเนินงาน
  • สิ่งที่เป็นนามธรรมและการปรับแต่งของมันเท่านั้นที่รู้จักอินเตอร์เฟซการใช้งาน

ป้อนคำอธิบายรูปภาพที่นี่

ข้อสังเกตเพิ่มเติมเกี่ยวกับความสับสนบ่อยครั้ง

รูปแบบนี้คล้ายกับรูปแบบของอะแดปเตอร์: สิ่งที่เป็นนามธรรมนำเสนออินเทอร์เฟซที่แตกต่างกันสำหรับการใช้งานและใช้การจัดองค์ประกอบเพื่อทำเช่นนั้น แต่:

ความแตกต่างที่สำคัญระหว่างรูปแบบเหล่านี้อยู่ในความตั้งใจของพวกเขา
- Gamma & al ใน" รูปแบบการออกแบบองค์ประกอบของซอฟต์แวร์ OO ที่ใช้ซ้ำได้ " , 1995

ในหนังสือน้ำเชื้อที่ยอดเยี่ยมนี้เกี่ยวกับรูปแบบการออกแบบผู้เขียนยังสังเกตเห็นว่า:

  • มักใช้อะแดปเตอร์เมื่อพบความไม่ลงรอยกันและการเชื่อมต่อจะไม่คาดคิด
  • สะพานถูกนำมาใช้ตั้งแต่เริ่มต้นของการออกแบบเมื่อคาดว่าชั้นเรียนจะมีวิวัฒนาการอย่างอิสระ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.