Я взагалі не розумію схему дизайну «містків». Я переглянув різні веб-сайти, але вони не допомогли.
Хтось може мені допомогти в розумінні цього?
Я взагалі не розумію схему дизайну «містків». Я переглянув різні веб-сайти, але вони не допомогли.
Хтось може мені допомогти в розумінні цього?
Відповіді:
У ООП ми використовуємо поліморфізм, тому абстракція може мати кілька реалізацій. Давайте розглянемо наступний приклад:
//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
У цій чудовій семінарній книзі про дизайнерські зразки автори також зауважують, що: