Hiểu mẫu thiết kế cầu


24

Tôi hoàn toàn không hiểu mẫu thiết kế "cây cầu". Tôi đã đi qua các trang web khác nhau, nhưng họ đã không giúp đỡ.

Ai có thể giúp tôi hiểu điều này?


2
Tôi cũng không hiểu. Mong được nhìn thấy câu trả lời :)
Violet Gi hươu cao cổ

Có rất nhiều trang web và sách mô tả các mẫu thiết kế ngoài kia. Tôi không nghĩ rằng có giá trị trong việc lặp lại những gì đã được viết, có lẽ bạn có thể hỏi một câu hỏi cụ thể. Tôi cũng mất một thời gian để hiểu nó, tiếp tục chuyển đổi giữa các nguồn và ví dụ khác nhau.
Helena

Câu trả lời:


16

Trong OOP, chúng tôi sử dụng đa hình để một sự trừu tượng có thể có nhiều triển khai. Hãy xem ví dụ sau:

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

Một yêu cầu mới được đưa ra và cần mang lại viễn cảnh tăng tốc của các đoàn tàu, vì vậy hãy thay đổi mã như dưới đây.

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

Đoạn mã trên không thể duy trì được và thiếu khả năng sử dụng lại (giả sử chúng ta có thể sử dụng lại cơ chế tăng tốc cho cùng một nền tảng theo dõi). Đoạn mã sau áp dụng mô hình cầu và tách hai khái niệm trừu tượng khác nhau, vận chuyển tàu hỏatăng tốc .

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
Ví dụ rất hay. Tôi sẽ thêm hai xu của mình: đây là ví dụ rất hay về việc thích sáng tác hơn thừa kế
zzfima

1
Đối với những gì nó có giá trị, tôi nghĩ rằng nó nên là Monorailvì nó không thực sự là hai từ, đó là một từ (ghép). MonoRail sẽ là một lớp con của Rail thay vì một loại đường ray khác (chính là nó). Cũng giống như chúng ta sẽ không sử dụng SunShinehoặc CupCake, họ sẽ SunshineCupcake
ErikE

Dòng này "hai khái niệm trừu tượng khác nhau, vận chuyển và tăng tốc" giúp chỉ ra đâu là hai thứ bậc và điều chúng ta cố gắng giải quyết là làm cho chúng thay đổi độc lập.
wangdq

11

Trong khi hầu hết các mẫu thiết kế có tên hữu ích, tôi thấy tên "Cầu" không trực quan liên quan đến những gì nó làm.

Về mặt khái niệm, bạn đẩy các chi tiết triển khai được sử dụng bởi một hệ thống phân cấp lớp vào một đối tượng khác, thường là với hệ thống phân cấp riêng của nó. Bằng cách đó, bạn sẽ loại bỏ sự phụ thuộc chặt chẽ vào các chi tiết triển khai đó và cho phép các chi tiết của việc triển khai đó thay đổi.

Ở quy mô nhỏ, tôi thích điều này với việc sử dụng một mẫu chiến lược theo cách bạn có thể thực hiện một hành vi mới. Nhưng thay vì chỉ gói một thuật toán như thường thấy trong một chiến lược, đối tượng triển khai thường có nhiều tính năng hơn. Và khi bạn áp dụng khái niệm này cho toàn bộ hệ thống phân cấp lớp, mẫu lớn hơn sẽ trở thành Cầu. (Một lần nữa, ghét tên).

Nó không phải là một mô hình mà bạn sẽ sử dụng hàng ngày, nhưng tôi đã thấy nó hữu ích khi quản lý một sự bùng nổ tiềm năng của các lớp có thể xảy ra khi bạn có nhu cầu (rõ ràng) cho nhiều kế thừa.

Đây là một ví dụ thực tế:

Tôi có một công cụ RAD cho phép bạn thả và cấu hình các điều khiển trên bề mặt thiết kế, vì vậy tôi có một mô hình đối tượng như thế này:

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

Và như vậy, với có lẽ một tá điều khiển.

Nhưng sau đó, một yêu cầu mới được thêm vào để hỗ trợ nhiều chủ đề (look-n-feel). Hãy nói rằng chúng tôi có các chủ đề sau: Win32, WinCE, WinPPC, WinMo50, WinMo65. Mỗi chủ đề sẽ có các giá trị hoặc cách triển khai khác nhau cho các hoạt động liên quan đến kết xuất như DefaultFont, DefaultBackColor, BorderWidth, DrawFrame, DrawScrollThumb, v.v.

Tôi có thể tạo một mô hình đối tượng như thế này:

Win32TextboxWidget : TextboxWidget

Win32ListWidget : ListWidget

vv, cho một loại điều khiển

WinCETextboxWidget : TextboxWidget

WinCEListWidget : ListWidget

vv, cho loại điều khiển lẫn nhau (một lần nữa)

Bạn có được ý tưởng, bạn sẽ có được sự bùng nổ của # các vật dụng nhân với số chủ đề. Điều này làm phức tạp nhà thiết kế RAD bằng cách làm cho nó nhận thức được từng chủ đề. Thêm vào đó, việc thêm các chủ đề mới buộc nhà thiết kế RAD phải được sửa đổi. Hơn nữa, có rất nhiều triển khai phổ biến trong một chủ đề mà nó sẽ rất tuyệt khi kế thừa, nhưng các điều khiển đã được kế thừa từ một cơ sở chung ( Widget).

Vì vậy, thay vào đó, những gì tôi đã làm là tạo một hệ thống phân cấp đối tượng riêng biệt thực hiện chủ đề. Mỗi widget sẽ giữ một tham chiếu đến đối tượng thực hiện các hoạt động kết xuất. Trong nhiều văn bản, lớp này có hậu tố Implnhưng tôi đã đi chệch khỏi quy ước đặt tên đó.

Vì vậy, bây giờ TextboxWidgettrông của tôi như thế này:

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

Và tôi có thể có các họa sĩ khác nhau của tôi kế thừa cơ sở cụ thể theo chủ đề của tôi, điều mà trước đây tôi không thể làm:

Win32WidgetPainter
+ DefaultFont
+ DefaultFontSize
+ DefaultColors
+ DrawFrame
+ Etc

Win32TextboxPainter : Win32WidgetPainter

Win32ListPainter : Win32WidgetPainter

Một trong những điều tuyệt vời là tôi có thể tải động các triển khai trong thời gian chạy, cho phép tôi thêm nhiều chủ đề như tôi muốn mà không cần thay đổi phần mềm cốt lõi. Nói cách khác, "việc thực hiện của tôi có thể thay đổi độc lập với sự trừu tượng".


Tôi không hiểu làm thế nào đây có thể là mô hình cầu? Khi bạn thêm một thành phần mới vào hệ thống phân cấp Widget, bạn buộc phải thêm Widget mới đó vào TẤT CẢ các họa sĩ (Win32NewWidgetPainter, PPCNewWidgetPainter). Đây KHÔNG phải là hai hệ thống phân cấp phát triển độc lập. Đối với một mẫu cầu nối phù hợp, bạn sẽ không phân lớp lớp PlatformWidgetPainter cho mỗi tiện ích, thay vào đó, nó sẽ nhận được một "mô tả vẽ" Widget.
Mazyod

Cảm ơn các phản hồi, bạn đã đúng. Đó là những năm trước tôi đã đăng bài này, và xem lại nó bây giờ tôi sẽ nói nó mô tả cây cầu tốt cho đến khi cuối cùng tôi bắt nguồn Win32TextboxPainterWin32ListPainter từ đâu Win32WidgetPainter. Bạn có thể có một cây thừa kế ở phía bên thực hiện, nhưng nó phải là chung chung hơn (có lẽ StaticStyleControlPainter, EditStyleControlPainterButtonStyleControlPainter) với bất kỳ hoạt động nguyên thủy cần ghi đè khi cần thiết. Điều này gần với mã thực hơn mà tôi đã dựa vào ví dụ trên.
tcarvin

3

Cây cầu dự định tách rời một sự trừu tượng khỏi việc thực hiện cụ thể của nó , để cả hai có thể thay đổi độc lập:

  • tinh chỉnh sự trừu tượng với phân lớp
  • cung cấp các triển khai khác nhau, cũng bằng cách phân lớp, mà không cần phải biết cả sự trừu tượng cũng như các sàng lọc của nó.
  • nếu cần, chọn trong thời gian chạy là cách thực hiện phù hợp nhất .

Cây cầu đạt được điều này bằng cách sử dụng thành phần:

  • sự trừu tượng tham chiếu (tham chiếu hoặc con trỏ) đến một đối tượng thực hiện
  • sự trừu tượng và các sàng lọc của nó chỉ được biết đến giao diện thực hiện

nhập mô tả hình ảnh ở đây

Nhận xét bổ sung về một sự nhầm lẫn thường xuyên

Mẫu này rất giống với mẫu bộ điều hợp: sự trừu tượng cung cấp một giao diện khác để thực hiện và sử dụng bố cục để làm như vậy. Nhưng:

Sự khác biệt chính giữa các mẫu này nằm ở ý định của chúng
- Gamma & al, trong " Mẫu thiết kế, yếu tố của phần mềm OO có thể tái sử dụng " , 1995

Trong cuốn sách xuất sắc về mẫu thiết kế này, các tác giả cũng nhận thấy rằng:

  • bộ điều hợp thường được sử dụng khi phát hiện sự không tương thích và khớp nối không lường trước được
  • cầu được sử dụng từ khi bắt đầu thiết kế, khi dự kiến ​​các lớp sẽ phát triển độc lập.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.