Що робить ітератор схемою дизайну?


9

Мені було цікаво, що саме те, що робить Ітератор особливим у порівнянні з іншими подібними конструкціями, і що змусило Банку чотирьох перерахувати його як модель дизайну.

Ітератор заснований на поліморфізмі (ієрархії колекцій із загальним інтерфейсом) та розділенні проблем (повторення над колекціями має бути незалежним від способу структурування даних).

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

Напевно, ми можемо знайти ще багато подібних прикладів, таких як Writer, Painter, Encoder і, мабуть, кращі, які працюють однаково. Однак я ніколи не чув, щоб жодне з них називалося «Шаблон дизайну».

Отже, що робить Ітератор особливим?

Це факт, що він складніший, оскільки для збереження поточної позиції в колекції він потребує змінного стану? Але тоді стан, що змінюється, зазвичай не вважається бажаним.


Щоб уточнити свою думку, дозвольте навести більш детальний приклад.

Ось наша проблема дизайну:

Скажімо, у нас є ієрархія класів і операція, визначена на об'єктах цих класів. Інтерфейс цієї операції однаковий для кожного класу, але реалізації можуть бути абсолютно різними. Також передбачається, що має сенс застосовувати операцію кілька разів на одному об’єкті, скажімо, з різними параметрами.

Ось розумне рішення нашої дизайнерської задачі (практично узагальнення схеми ітератора):

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

Приклад:

Є базовий клас або інтерфейс MathObject(дурне ім'я, я знаю, можливо, хтось має кращу ідею.) З похідними класами MyIntegerі MyMatrix. Для кожної MathObjectоперації Powerслід визначити, що дозволяє обчислити квадрат, куб тощо. Тож ми могли написати (на Java):

MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125

5
"Діаграма класу була б однаковою" - і що? Шаблон дизайну не є діаграмою класу. Це абстракція високого рівня для класу рішень повторюваної проблеми.
Док Браун

@DocBrown: Так, але це не математичні операції, записування об'єктів у файл, графічний вихід або кодування даних, що повторюються, як ітерація?
Френк Пуффер

Вибір шаблону дизайну є суб'єктивним (тобто в очах "дизайнерів" чи людей, які судять про дизайн). Назви шаблонів дизайну мають бути доменно-агностичними (щоб ми не відволікалися на думку, що це домен). Лише моя думка, я не можу цитувати жодних відгуків.
rwong

@FrankPuffer Якщо ви накреслили загальне рішення для запису об'єктів у файл, ви можете записати своє рішення і назвати його Шаблон написання об'єктів, якщо це корисно.
Брандін

3
Ти це передумуєш. Шаблон дизайну - добре відоме рішення загальної обчислювальної проблеми, і це все. Ви використовуєте шаблон, коли зможете розпізнати та застосувати переваги, які він надає.
Роберт Харві

Відповіді:


9

Більшість моделей із книги GoF мають спільне:

  • вони вирішують основні задачі проектування, використовуючи об'єктно-орієнтовані засоби
  • люди часто стикаються з подібними проблемами у довільних програмах, незалежно від домену чи бізнесу
  • вони є рецептами для того, щоб зробити код більш багаторазовим, часто роблячи його більш твердим
  • вони представляють канонічні рішення цих проблем

Проблеми, що вирішуються цими шаблонами, настільки основні, що багато розробників розуміють їх в основному як обхідні шляхи для відсутніх функцій мови програмування , що є IMHO справедливою точкою зору. риси, як сьогодні).

Шаблон ітератора добре вписується в цей опис: він вирішує основну проблему, яка виникає дуже часто, незалежно від будь-якого конкретного домену, і, як ви самі писали, це хороший приклад для «розділення проблем». Як ви точно знаєте, підтримка прямого ітератора - це те, що ви знайдете в багатьох сучасних мовах програмування сьогодні.

Тепер порівняйте це з обраними вами проблемами:

  • запис у файл - це IMHO просто недостатньо "базовий". Це дуже специфічна проблема. Також немає хорошого канонічного рішення - існує безліч різних підходів, як записати у файл, і немає чітких "найкращих практик".
  • Painter, Encoder: що б ви не мали на увазі з цього приводу, ці проблеми для мене виглядають ще менш базовими, і навіть не залежать від домену.
  • функція "живлення" доступна для різних типів об'єктів: на перший погляд, це могло б стояти за схемою, але запропоноване вами рішення не переконує мене - це більше схоже на спробу підключити функцію живлення до чогось подібного до ітератор шаблон. Я реалізував багато коду з інженерними розрахунками, але не можу пригадати ситуацію, коли підхід, подібний до вашого об'єкта функціонування, міг би мені допомогти (однак, ітератори - це те, з чим я маю мати справу щодня).

Більше того, я не бачу у вашому прикладі функції живлення нічого, що не могло б трактуватися як застосування схеми стратегії чи схеми команд, а це означає, що ці основні частини вже є у книзі GoF. Краще рішення може містити або перевантажування операторів, або методи розширення, але це речі, залежать від мовних особливостей, і саме те "OO означає", яке використовує "Gang", не може надати.


The problems solved by these patterns are so basic that many developers think their main purpose is to be workarounds for missing programming language features- Іронія в тому, що розробники програмного забезпечення регулярно використовують моделі дизайну програмного забезпечення, яким вже 20 років, і все ще вважають, що вони пишуть сучасний код.
Роберт Харві

@RobertHarvey: Я не думаю, що багато розробників сьогодні реалізовують ітераторську схему "способом OO", запропонованим GoF. Вони зазвичай реалізують його засобами, що надаються мовою, або стандартною ліб (наприклад, у C # за допомогою IEnumerableта yield). Але для інших моделей GoF, те, що ви написали, можливо, може бути правдою.
Док Браун

1
Відповідно до workarounds for missing programming language features: blog.plover.com/prog/johnson.html
jrw32982 підтримує Моніку

8

Визначення схеми Крістофера Олександра:

Кожна закономірність описує проблему, яка виникає знову і знову в нашому середовищі, а потім описує серцевину рішення цієї проблеми […]

Яку проблему вирішують ітератори?

Намір: Наведіть спосіб доступу до елементів сукупного об'єкта послідовно, не піддаючи його основного подання.

Застосування: використовуйте шаблон ітератора

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

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

І добре, що шаблон Ітератора існує. Погані речі трапляються, якщо ви не користуєтесь цим. Мій улюблений антиприклад - Perl. Тут кожна колекція (масив чи хеш) включає стан ітератора як частину колекції. Чому це погано? Ми можемо легко повторити хеш з часом - кожен цикл:

while (my ($key, $value) = each %$hash) {
  say "$key => $value";
}

Але що робити, якщо ми називаємо функцію в тілі циклу?

while (my ($key, $value) = each %$hash) {
  do_something_with($key, $value, $hash);
}

Ця функція тепер може робити майже все, що завгодно, крім:

  • додайте або видаліть хеш-записи, оскільки вони непередбачувано змінять порядок ітерації (у C ++ - говоріть, вони недійсні ітератору).
  • повторіть ту саму хеш-таблицю, не роблячи її копію, оскільки вона б використовувала той самий стан ітерації. На жаль

Якщо викликана функція повинна використовувати ітератор, поведінка нашого циклу стає невизначеною. Це проблема. І шаблон ітератора має рішення: помістити всі ітераційні стану в окремий об'єкт, створений за ітерацією.

Так, звичайно, модель ітератора пов'язана з іншими шаблонами. Наприклад, як інстатується ітератор? У Java у нас є загальний Iterable<T>і Iterator<T>інтерфейс. Конкретний подібний ArrayList<T>ітератор створює певний тип ітератора, тоді як HashSet<T>може поставити зовсім інший тип ітератора. Це мені дуже нагадує абстрактний заводський зразок, де Iterable<T>абстрактна фабрика і Iteratorпродукт.

Поліморфний ітератор також може бути інтерпретований як приклад стратегії. Наприклад, дерево може пропонувати різні ітератори (попереднє замовлення, замовлення, після замовлення,…). Зовні всі вони поділяють інтерфейс ітератора та елементи виходу в деякій послідовності. Код клієнта повинен залежати лише від інтерфейсу ітератора, а не від певного алгоритму переходу дерева.

Візерунки не існують ізольовано, незалежно один від одного. Деякі зразки є різними рішеннями однієї проблеми, а деякі зразків описують одне і те ж рішення в різних контекстах. Деякі зразки передбачають іншу. Крім того , картина простір не закривається при включенні останньої сторінки книги Patterns дизайну (див також ваш попередній питання чи Банда чотирьох ретельно вивчити «Pattern Space»? ). Шаблони, описані в книзі "Шаблони дизайну", дуже гнучкі та широкі, відкриті для нескінченних змін і, безумовно, не єдині існуючі.

Перелічені вами концепції (написання, малювання, кодування) не є шаблонами, оскільки вони не описують поєднання проблем і рішення. Таке завдання, як "мені потрібно записати дані" або "мені потрібно кодувати дані", насправді не є проблемою дизайну і не включає рішення; "рішення", яке складається лише з "Я знаю, я створити клас письменника" безглуздо. Але якщо у нас є проблема на кшталт "Я не хочу, щоб на екрані була намальована напівфабрикована графіка", то може існувати схема: "Я знаю, я буду використовувати графіку з двома буферами!"


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