Я взагалі не розумію схему дизайну «містків». Я переглянув різні веб-сайти, але вони не допомогли.
Хтось може мені допомогти в розумінні цього?
Я взагалі не розумію схему дизайну «містків». Я переглянув різні веб-сайти, але вони не допомогли.
Хтось може мені допомогти в розумінні цього?
Відповіді:
У ООП ми використовуємо поліморфізм, тому абстракція може мати кілька реалізацій. Давайте розглянемо наступний приклад:
//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*/}
Monorailоскільки це насправді не два слова, це одне (складене) слово. MonoRail був би деяким підкласом Rail замість іншого виду залізниць (яким він є). Так само , як ми не будемо використовувати SunShineабо CupCakeвони були б SunshineіCupcake
Хоча більшість моделей дизайну мають корисні назви, я вважаю, що ім'я "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
Одне з приємних речей - це те, що я можу динамічно завантажувати реалізації під час виконання, дозволяючи мені додавати стільки тем, скільки мені потрібно, не змінюючи основного програмного забезпечення. Іншими словами, моя "реалізація може змінюватись незалежно від абстракції".
Win32TextboxPainterі Win32ListPainter звідки Win32WidgetPainter. Ви можете мати дерево спадкування на стороні реалізації, але воно повинно бути більш загальним (можливо StaticStyleControlPainter, EditStyleControlPainterі ButtonStyleControlPainter) з будь-якими необхідними примітивними операціями перевизначені при необхідності. Це ближче до реального коду, на якому я спирався на прикладі.
Міст має намір відокремити абстракцію від конкретної реалізації , щоб обидва могли змінюватись незалежно:
Міст досягають цього за допомогою складу:
Додаткові зауваження щодо частої плутанини
Ця модель дуже схожа на модель адаптера: абстракція пропонує інший інтерфейс для реалізації і використовує композицію для цього. Але:
Ключова відмінність цих моделей полягає в їхніх намірах
- Gamma & al, у " Дизайнерських моделях, елементі програмного забезпечення для багаторазового використання " , 1995
У цій чудовій семінарній книзі про дизайнерські зразки автори також зауважують, що: