Мне было интересно, что же делает Итератор особенным по сравнению с другими подобными конструкциями, и это заставило Банду Четырех перечислить его как шаблон проектирования.
Итератор основан на полиморфизме (иерархия коллекций с общим интерфейсом) и разделении задач (итерации по коллекциям должны быть независимы от структуры данных).
Но что, если мы заменим иерархию коллекций, например, иерархией математических объектов (целое число, число с плавающей запятой, комплекс, матрица и т. Д.), А итератор - классом, представляющим некоторые связанные операции над этими объектами, например, степенные функции. Диаграмма классов будет такой же.
Возможно, мы могли бы найти еще много похожих примеров, таких как Writer, Painter, Encoder и, возможно, лучших, которые работают одинаково. Однако я никогда не слышал, чтобы кто-либо из них назывался Design Pattern.
Так что же делает Итератор особенным?
Является ли тот факт, что это сложнее, потому что он требует изменяемого состояния для хранения текущей позиции в коллекции? Но тогда изменчивое состояние обычно не считается желательным.
Чтобы прояснить мою точку зрения, позвольте мне привести более подробный пример.
Вот наша проблема дизайна:
Допустим, у нас есть иерархия классов и операция, определенная для объектов этих классов. Интерфейс этой операции одинаков для каждого класса, но реализации могут быть совершенно разными. Также предполагается, что имеет смысл применять операцию несколько раз к одному и тому же объекту, скажем, с разными параметрами.
Вот разумное решение для нашей проблемы проектирования (практически обобщение шаблона итератора):
Для разделения задач реализации операции не следует добавлять как функции к исходной иерархии классов (объекты операндов). Поскольку мы хотим применить операцию несколько раз к одному и тому же операнду, она должна быть представлена объектом, содержащим ссылку на операнд, а не просто функцией. Поэтому объект операнда должен предоставлять функцию, которая возвращает объект, представляющий операцию. Этот объект предоставляет функцию, которая выполняет фактическую операцию.
Пример:
Есть базовый класс или интерфейс 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
источник
Ответы:
Большинство шаблонов из книги GoF имеют следующие общие черты:
Проблемы, решаемые этими шаблонами, настолько просты, что многие разработчики понимают их главным образом как обходные пути для отсутствующих возможностей языка программирования , что, по-моему, является верной точкой зрения (обратите внимание, что книга GoF относится к 1995 году, где Java и C ++ не предлагали так много особенности как сегодня).
Шаблон итератора хорошо вписывается в это описание: он решает основную проблему, которая возникает очень часто, независимо от какой-либо конкретной области, и, как вы сами написали, это хороший пример для «разделения интересов». Как вы наверняка знаете, прямая поддержка итераторов - это то, что вы сегодня найдете во многих современных языках программирования.
Теперь сравните это с проблемами, которые вы выбрали:
Более того, я не вижу в вашем примере степенной функции ничего такого, что не могло бы быть истолковано как применение шаблона стратегии или шаблона команды, что означает, что эти основные части уже есть в книге GoF. Лучшее решение может содержать методы перегрузки операторов или расширения, но это те вещи, которые зависят от языковых возможностей, и это именно то, что «ОО означает», используемый «бандой», не может обеспечить.
источник
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 лет, и при этом все еще верят, что они пишут современный код.IEnumerable
иyield
). Но для других паттернов GoF то, что вы написали, может быть правдой.workarounds for missing programming language features:
blog.plover.com/prog/johnson.html«Банда четырех» цитирует определение паттерна Кристофера Александра:
Какую проблему решают итераторы?
Таким образом, можно утверждать, что шаблон итератора по определению зависит от области для коллекций. И это совершенно нормально. Другие шаблоны, такие как шаблон интерпретатора, зависят от домена для конкретных доменов языков, фабричные шаблоны зависят от домена для создания объектов,…. Конечно, это довольно глупое понимание «предметной области». Пока это повторяющаяся пара проблема-решение, мы можем назвать это шаблоном.
И хорошо, что шаблон Iterator существует. Плохие вещи случаются, если вы им не пользуетесь. Мой любимый анти-пример - Perl. Здесь каждая коллекция (массив или хэш) включает в себя состояние итератора как часть коллекции. Почему это плохо? Мы можем легко перебрать хеш с циклом while-each:
Но что, если мы вызываем функцию в теле цикла?
Эта функция теперь может делать почти все, что ей нужно, кроме:
Если вызываемая функция должна использовать итератор, поведение нашего цикла становится неопределенным. Это проблема. И шаблон итератора имеет решение: поместите все состояние итерации в отдельный объект, который создается для каждой итерации.
Да, конечно, шаблон итератора связан с другими шаблонами. Например, как создается экземпляр итератора? В Java у нас есть универсальный
Iterable<T>
иIterator<T>
интерфейс. Конкретное итерируемое подобноеArrayList<T>
создает определенный тип итератора, тогда как aHashSet<T>
может предоставлять совершенно другой тип итератора. Это очень напоминает мне абстрактный шаблон фабрики, гдеIterable<T>
абстрактная фабрикаIterator
- это продукт.Полиморфный итератор также можно интерпретировать как пример шаблона стратегии. Например, дерево может предлагать различные типы итераторов (предварительный порядок, порядок, пост-порядок, ...). Внешне все они будут совместно использовать интерфейс итератора и давать элементы в некоторой последовательности. Код клиента должен зависеть только от интерфейса итератора, а не от какого-либо конкретного алгоритма обхода дерева.
Шаблоны не существуют изолированно, независимо друг от друга. Некоторые шаблоны - это разные решения одной и той же проблемы, а некоторые шаблоны описывают одно и то же решение в разных контекстах. Некоторые модели подразумевают другое. Кроме того, пространство шаблонов не закрывается, когда вы переворачиваете последнюю страницу книги «Шаблоны проектирования» (см. Также ваш предыдущий вопрос : «Банда четырех» тщательно исследовала «Пространство шаблонов»? ). Шаблоны, описанные в книге «Шаблоны проектирования», очень гибкие и широкие, открыты для бесконечных вариаций и, безусловно, не являются единственными существующими шаблонами.
Концепции, которые вы перечисляете (написание, рисование, кодирование), не являются шаблонами, потому что они не описывают комбинацию проблема-решение. Задача типа «мне нужно записать данные» или «мне нужно кодировать данные» на самом деле не является проблемой проектирования и не включает в себя решение; «решение», которое состоит только из «я знаю, я создам класс Writer», не имеет смысла. Но если у нас возникнет проблема типа «Я не хочу, чтобы на экране была нарисована половина графики, то может существовать шаблон:« Я знаю, я буду использовать графику с двойной буферизацией! »
источник