Питання для новачків про шаблон дизайну дизайнера


18

Я читав статтю з програмування, і в ній згадувався шаблон декоратора. Я певний час програмував, але без будь-якої формальної освіти чи навчання, але намагаюся дізнатися про стандартні зразки та таке.

Тож я подивився на «Оформлювач» і знайшов у ньому статтю у Вікіпедії . Зараз я розумію концепцію візерунка «Декоратор», але мене цей зрив трохи збентежив:

Як приклад розглянемо вікно у віконній системі. Щоб дозволити прокручування вмісту вікна, ми, можливо, бажаємо додати до нього горизонтальні чи вертикальні смуги прокрутки. Припустимо, вікна представлені екземплярами класу Window, і припустимо, що цей клас не має функціональних можливостей для додавання смуг прокрутки. Ми можемо створити підклас ScrollingWindow, який їх надає, або ми можемо створити ScrollingWindowDecorator, який додає цю функціональність до існуючих об'єктів Window. На даний момент будь-яке рішення було б добре.

Тепер припустимо, що ми також бажаємо можливості додавати межі до наших вікон. Знову ж таки, наш оригінальний клас Window не підтримує. Підклас ScrollingWindow тепер створює проблему, оскільки він фактично створив новий вид вікна. Якщо ми хочемо додати підтримку кордону до всіх вікон, ми повинні створити підкласи WindowWithBorder і ScrollingWindowWithBorder. Очевидно, що ця проблема загострюється з кожною новою функцією, яку потрібно додати. Для рішення декоратора ми просто створюємо новий BorderedWindowDecorator - під час виконання ми можемо прикрасити існуючі вікна за допомогою ScrollingWindowDecorator або BorderedWindowDecorator або обох, як вважаємо за потрібне.

Добре, коли вони кажуть додати межі до всіх вікон, чому б просто не додати функціональність до оригінального класу Window, щоб дозволити можливість? Як я це бачу, підкласифікація - це лише додавання певної функціональності до класу або переосмислення методу класу. Якщо мені потрібно було додати функціонал до всіх існуючих об'єктів, чому б я просто не змінив суперклас для цього?

У статті був ще один рядок:

Візерунок декораторів є альтернативою підкласифікації. Підкласифікація додає поведінку під час компіляції, і зміна впливає на всі екземпляри оригінального класу; декорування може забезпечити нову поведінку під час виконання окремих об’єктів.

Я не знаходжусь там, де вони кажуть "... зміна впливає на всі екземпляри початкового класу" - як підкласифікація змінює батьківський клас? Чи не в цьому вся суть підкласифікації?

Я припускаю, що стаття, як і багато Вікі, просто не написана чітко. Я бачу корисність декоратора в останньому рядку - "... забезпечити нову поведінку під час виконання окремих об'єктів".

Не читаючи про цю закономірність, якщо мені потрібно змінити поведінку під час виконання окремих об'єктів, я, мабуть, вбудував би деякі методи в над- або підклас, щоб увімкнути / відключити цю поведінку. Будь ласка, допоможіть мені реально зрозуміти корисність декоратора, і чому моє мислення для новачків є хибним?


оцінюйте правку, Вальтере ... фей, я "хлопці" використовував не для виключення жінок, а як неформальне привітання.
Jim Jim

Редагуванням було б просто видалити привітання загалом. Це не стандартний протокол SO / SE, щоб використовувати його в питаннях [не хвилюйтеся, ми не вважаємо, що це грубо перейти прямо в питання]
Farrell

класно, дякую! Просто він позначив це "видаленням статі", і я б ненавиджу, щоб хтось міг подумати, що я мізогініст чи щось таке !! :)
Джим

Я активно використовую їх, щоб уникнути тих юридичних процедурних речей, які називаються "послуги": medium.com/@wrong.about/…
Westlo

Відповіді:


13

Шаблон декоратора - це той, що надає перевагу композиції над успадкуванням [інша парадигма OOP, яка корисна для ознайомлення]

Основна перевага декоративного шаблону - над підкласифікацією - це дозволити більше варіантів змішування та відповідності. Наприклад, якщо у вас є 10 різних типів поведінки, які може мати вікно, то це означає - з підкласифікацією - вам потрібно створювати кожну іншу комбінацію, яка також неминуче включає багато разів використання коду.
Однак, що станеться, коли ви вирішите додати нову поведінку?

За допомогою декоратора ви просто додаєте новий клас, який описує таку поведінку, і це все - шаблон дозволяє ефективно вносити це без будь-яких змін до решти коду.
З підкласовим класом у вас на руках кошмар.
Одне питання, яке ви задали, було "як підкласифікація змінює батьківський клас?" Це не те, що він змінює батьківський клас; коли він говорить про екземпляр, це означає, що будь-який об'єкт, який ви "примірник" [якщо ви використовуєте Java або C #, наприклад, за допомогою newкоманди]. Те, про що йдеться, полягає в тому, що коли ви додаєте ці зміни до класу, у вас немає вибору, щоб ця зміна була там, навіть якщо вона насправді не потрібна.

Крім того, ви можете розмістити всю його функціональність в одному класі, увімкнувши / вимикаючи його через прапори ... але це закінчується одним класом, який збільшується і збільшується в міру зростання вашого проекту.
Незвичайно починати свій проект саме так, і коли ви потрапляєте на ефективну критичну масу, перетворюйте на декоративну схему.

Цікавий момент, який слід зауважити: ви можете додати одну і ту ж функціональність кілька разів; таким чином, ви можете, наприклад, мати вікно з подвійною, потрійною або будь-якою сумою або межами, як вам потрібно.

Основним моментом шаблону є включення змін часу виконання: ви можете не знати, як потрібно виглядати вікно до запуску програми, і це дозволяє легко змінювати його. Зрозуміло, це можна зробити за допомогою підкласингу, але не так добре.
І нарешті, це дозволяє додавати функціональність до класів, які ви не зможете редагувати - наприклад, у закритих / заключних класах або тих, що надаються з інших API


2
Дякую, Фаррелл - коли ти кажеш "Альтернативно, ти можеш розмістити всю його функціональність в одному класі, увімкнувши / вимкнувши його за допомогою прапорів ... Але це закінчується тим, що один клас стає все більшим і більшим у міру зростання вашого проекту". що змусило мене натиснути. Я думаю, що частина питання полягає в тому, що я ніколи не працював над класом, який набув такого великого значення, що декоратор мав сенс для мене. Думаючи про велике, я точно можу побачити переваги ... дякую!
Jim Jim

Крім того, частина про закриті класи має багато сенсу ... дякую!
Jim Jim

3

Розглянемо можливості додавання смуг прокрутки та меж з підкласом. Якщо ви хочете використовувати всі можливості, ви отримуєте чотири класи (Python):

class Window(object):
    def draw(self):
        "do actual drawing of window"

class WindowWithScrollBar(Window):
    def draw(self):
        Window.draw(self)
        "do actual drawing of scrollbar"

class WindowWithBorder(Window):
    def draw(self):
        Window.draw(self)
        "do actual drawing of border"

class WindowWithScrollBarAndBorder(Window):
    def draw(self):
        WindowWithScrollBar.draw(self)
        WindowWithBorder.draw(self)

Тепер у WindowWithScrollBarAndBorder.draw()вікні виводиться двічі, і вдруге воно може або не може замінити вже намальовану смугу прокрутки, залежно від реалізації. Таким чином, ваш похідний код тісно пов'язаний з реалізацією іншого класу, і вам потрібно дбати про це щоразу, коли ви змінюєте Windowповедінку класу. Рішенням було б скопіювати і вставити код із суперкласів до похідних класів та пристосувати його до похідних класів, але кожну зміну суперкласу потрібно знову копіювати і знову коригувати, тому знову отримані класи є щільно з'єднані з базовим класом (через необхідність копіювання-вставки-коригування). Інша проблема полягає в тому, що якщо вам потрібна ще одна властивість, яку вікно може мати, а може і не мати, вам потрібно подвоїти кожен клас:

class Window(object):
    ...

class WindowWithGimmick(Window):
    ...

class WindowWithScrollBar(Window):
    ...

class WindowWithBorder(Window):
    ...

class WindowWithScrollBarAndBorder(Window):
    ...

class WindowWithScrollBarAndGimmick(Window):
    ...

class WindowWithBorderAndGimmick(Window):
    ...

class WindowWithScrollBarAndBorderAndGimmick(Window):
    ...

Це означає для набору взаємно незалежних ознак f з | f | будучи кількістю функцій, ви повинні визначити 2 ** | f | класи, наприклад. якщо у вас є 10 функцій, ви отримуєте 1024 щільно сполучені класи. Якщо ви використовуєте шаблон декоратора, кожна функція отримує власний незалежний і нещільно зв'язаний клас, і у вас є лише 1 + | f | класи (це 11 для прикладу вище).


Бачите, моє мислення не було б продовжувати додавати класи - це було б додати функціональність до початкового (під) класу. так що у класі Window (об’єкт): у вас є scrollBar = true, border = false і т. д. ... Відповідь Farrell показує мені, де це може піти не так, однак - просто ведучи до роздутих класів і таких ... дякую за відповідь, +1!
Jim Jim

ну +1, як тільки у мене є представник, щоб зробити це!
Jim Jim

2

Я не є спеціалістом з цього конкретного шаблону, але, як я бачу, шаблон декоратора може бути застосований до класів, які можуть не мати можливості змінювати або підклас (вони можуть не бути вашим кодом і запечатані, наприклад, ). У вашому прикладі, що робити, якщо ви не написали клас Window, а просто споживаєте його? Поки клас Window має інтерфейс, і ви програмуєте проти цього інтерфейсу, ваш Decorator може використовувати той самий інтерфейс, але розширити функціонал.

Приклад, який ви згадуєте, насправді тут висвітлено досить акуратно:

Розширення функціональності об'єкта може здійснюватися статично (під час компіляції), використовуючи успадкування, однак, можливо, буде потрібно розширювати функціональність об'єкта динамічно (під час виконання), оскільки об'єкт використовується.

Розглянемо типовий приклад графічного вікна. Наприклад, щоб розширити функціональність графічного вікна, наприклад, додавши кадр у вікно, знадобиться розширити клас вікна для створення класу FramedWindow. Для створення обрамленого вікна необхідно створити об’єкт класу FramedWindow. Однак неможливо почати з простого вікна і розширити його функціональність під час виконання, щоб перетворитись у вікно з обрамленням.

http://www.oodesign.com/decorator-pattern.html

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.