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?
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?
Câu trả lời:
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ỏa và tă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*/}
Monorail
vì 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 SunShine
hoặc CupCake
, họ sẽ Sunshine
vàCupcake
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ố Impl
nhưng tôi đã đi chệch khỏi quy ước đặt tên đó.
Vì vậy, bây giờ TextboxWidget
trô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".
Win32TextboxPainter
và Win32ListPainter
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
, EditStyleControlPainter
và ButtonStyleControlPainter
) 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.
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:
Cây cầu đạt được điều này bằng cách sử dụng thành phần:
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: