CodeLAB
на главную карта сайта обратная связь

Популярные задачи:

#Числа Армстронга. (47339 hits)
#Утилиты. (116254 hits)
#Счетчик времени с точностью до микросекунд. (131162 hits)
#Поверхностное клонирование. (28610 hits)
#Обработка шаблонных писем. (57444 hits)
#Бинарный поиск в массиве и его разновидности. (174518 hits)
#Улучшение быстрой сортировки. (78921 hits)
#Циклический сдвиг массива или строки - 3 уникальных алгоритма. (396264 hits)
#Передача данных из основного во всплывающее-popup окно через POST. (118970 hits)
#Рисование тора. (36115 hits)
#Разбор строки. (274565 hits)
#Замена символа строки. (445129 hits)
#Вычисление минимального / максимального значения. (75736 hits)
#ООП на javascript: классы, наследование, инкапсуляция. (259873 hits)
#Вычисление значения полинома. (63551 hits)
#Заполнение 2-го выпадающего списка (select) в соответствии с выбором в первом. (47379 hits)
#"The Java Programming Language" Ken Arnold, James Gosling, David Holmes листинги, код, примеры из книги, исходники. (62078 hits)
#Как посчитать одинаковые пары за 1 проход (самая быстрая версия!). (3386 hits)
#Посчитать количество пар чисел (number of equal pairs). (7660 hits)
#Выборка конкретной записи из таблицы. (33805 hits)


Главная >> Каталог задач >> Паттерны >> Порождающие >> Прототип (Prototype)

Прототип (Prototype)

Aвтор:
Дата:
Просмотров: 119321
реализации(java: 2шт...) +добавить

Имя

«Паттерн
Prototype»

Прототип – паттерн, порождающий объекты.

Условия, Задача, Назначение

Определяет, задает виды создаваемых объектов с помощью интерфейса некоторого экземпляра-прототипа, и создает новые объекты путем копирования (клонирования) этого экземпляра.
Прототип – это единственный паттерн из серии «порождающих», который для создания новых объектов использует не явное инстанцирование, а клонирование. Что играет решающую роль в архитектуре данного паттерна. Дело в том, что любое, какое бы ни было, явное инстанцирование – в конечном счете, приводит созданию иерархических структур классов, где каждый потомок инстанцирует свой, отличный объект. Т.е. в случае явного инстанцирования, чтобы далее заменить один из создаваемых в итоге классов другим – нужно будет породить подкласс, переопределяющий эту процедуру создания. И вот только лишь использованием клонирования, вместо явного создания – мы можем этого избежать. Паттерн Прототип – активно это использует, чтобы абстрагировать конкретные классы, которые в итоге будут фигурировать в приложении.

Мотивация

Для большего понимания рассмотрим некоторый кусок архитектуры приложения некоего музыкального редактора. Построить музыкальный редактор удалось бы путем адаптации общего каркаса графических редакторов и добавления новых объектов, представляющих ноты, паузы и нотный стан. В каркасе редактора может присутствовать палитра инструментов для добавления в партитуру этих музыкальных объектов. Палитра может также содержать инструменты для выбора, перемещения и иных манипуляций с объектами. Так, пользователь, щелкнув, например, по значку четверти поместил бы ее тем самым в партитуру. Или, применив инструмент перемещения, сдвигал бы ноту на стане вверх или вниз, чтобы изменить ее высоту.

Предположим, что каркас предоставляет абстрактный класс Graphic для графических компонентов вроде нот и нотных станов, а также абстрактный класс Tool для определения инструментов в палитре. Кроме того, в каркасе имеется предопределенный подкласс GraphicTool для инструментов, которые создают графические объекты и добавляют их в документ. Однако класс GraphicTool создает некую проблему для проектировщика каркаса. Классы нот и нотных станов специфичны для нашего приложения, а класс GraphicTool принадлежит каркасу. Этому классу ничего неизвестно о том, как создавать экземпляры наших музыкальных классов и добавлять их в партитуру. Можно было бы породить от GraphicTool подклассы для каждого вида музыкальных объектов, но тогда оказалось бы слишком много классов, отличающихся только тем, какой музыкальный объект они инстанцируют. Мы знаем, что гибкой альтернативой порождению подклассов является композиция. Вопрос в том, как каркас мог бы воспользоваться ею для параметризации экземпляров GraphicTool классом того объекта Graphic, который предполагается создать.
Решение - заставить GraphicTool создавать новый графический объект, копируя или «клонируя» экземпляр подкласса класса Graphic. Этот экземпляр мы будем называть прототипом. GraphicTool параметризуется прототипом, который он должен клонировать и добавить в документ. Если все подклассы Graphic поддерживают операцию Clone, то GraphicTool может клонировать любой вид графических объектов.
Итак, в нашем музыкальном редакторе каждый инструмент для создания музыкального объекта - это экземпляр класса GraphicTool, инициализированный тем или иным прототипом. Любой экземпляр GraphicTool будет создавать музыкальный объект, клонируя его прототип и добавляя клон в партитуру.
Можно воспользоваться паттерном прототип, чтобы еще больше сократить число классов. Для целых и половинных нот у нас есть отдельные классы, но, быть может, это излишне. Вместо этого они могли бы быть экземплярами одного и того же класса, инициализированного разными растровыми изображениями и длительностями звучания. Инструмент для создания целых нот становится просто объектом класса GraphicTool, в котором прототип MusicalNote инициализирован целой нотой. Это может значительно уменьшить число классов в системе. Заодно упрощается добавление нового вида нот в музыкальный редактор.

Признаки применения, использования паттерна Прототип (Prototype)

Выражаясь общими словами снова можно дать уже довольно знакомый совет: «Используйте паттерн прототип, когда система не должна зависеть от того, как в ней создаются, компонуются и представляются продукты». Теперь более конкретные случаи:
  1. Инстанцируемые классы определяются во время выполнения, например, с помощью динамической загрузки. Поэтому вдвойне проблематично сначала динамически определять к какому конкретному классу относится объект, при этом не ошибиться определить именно самого потомка, а не родителя этого класса например, + затем еще затем активировать еще соответствующую фабрику для создания объектов такого рода и т.д.
  2. Для того чтобы избежать построения иерархий классов или фабрик, параллельных иерархии классов продуктов. Ну это известное дело – с ростом иерархии фабрик, растет соответственно иерархия продуктов, различных helper-ов и т.д.
  3. Экземпляры класса могут находиться в одном из не очень большого числа различных состояний, так что может оказаться целесообразней устанавливать соответствующее число прототипов и клонировать их, а не инстанцировать каждый раз класс вручную в подходящем состоянии.

Решение

Участники паттерна Прототип (Prototype)

  1. Prototype (Graphic) – прототип.
    Объявляет операцию собственного клонирования, и прикладной интерфейс для работы с объектами своего разряда.
  2. ConcretePrototype ( Staff - нотный стан, WholeNote - целая нота, HalfNote - половинная нота) - конкретный прототип.
    Реализует операцию собственного клонирования, при необходимости переопределяет api методы своего интерфейса.
  3. Client (GraphicTool) – клиент.
    Создает новый объект, обращаясь к прототипу с запросом клонировать себя, затем, пользуясь его интерфейсом, выполняет с полученным клоном установленные бизнес манипуляции.

Схема использования паттерна Прототип (Prototype)

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

Результаты

У прототипа те же самые результаты, что у абстрактной фабрики и строителя: он скрывает от клиента конкретные классы продуктов, уменьшая тем самым число известных клиенту имен. Кроме того, все эти паттерны позволяют клиентам работать со специфичными для приложения классами без модификаций.
Ниже перечислены дополнительные преимущества паттерна прототип:
  1. Добавление и удаление продуктов во время выполнения.
    Прототип позволяет включать новое семейство конкретных классов продуктов в систему, просто сообщив клиенту о новом экземпляре-прототипе. Это несколько более гибкое решение по сравнению с тем, что удастся сделать с помощью других порождающих паттернов, ибо клиент может устанавливать и удалять прототипы во время выполнения.
  2. Спецификация новых объектов путем изменения значений.
    Динамичные системы позволяют определять поведение за счет композиции объектов - например, путем задания значений переменных объекта, - а не с помощью определения новых классов. По сути дела, вы определяете новые виды объектов, инстанцируя уже существующие классы и регистрируя их экземпляры как прототипы клиентских объектов. Клиент может изменить поведение, делегируя свои обязанности прототипу, и соответственно стать в некотором роде параметризированным от прототипа. Что позволит, лишь подсовывая в качестве прототипа новый скомпонованный объект – легко менять поведение системы.
    Такой дизайн позволяет пользователям определять новые классы без программирования. Фактически клонирование объекта аналогично инстанцированию класса. Паттерн прототип может резко уменьшить число необходимых системе классов. В нашем музыкальном редакторе с помощью одного только класса GraphicTool удастся создать бесконечное разнообразие музыкальных объектов.
  3. Специфицирование новых объектов путем изменения структуры. Многие приложения строят объекты из крупных и мелких составляющих. Например, редакторы для проектирования печатных плат создают электрические схемы из подсхем. Такие приложения часто позволяют инстанцировать сложные, определенные пользователем структуры, скажем, для многократного использования некоторой подсхемы.
    Паттерн прототип поддерживает и такую возможность. Мы просто добавляем подсхему как прототип в палитру доступных элементов схемы. При условии, что объект, представляющий составную схему, реализует операцию Clone как глубокое копирование, схемы с разными структурами могут выступать в качестве прототипов, что позволит одинаково фабриковать как сложные составные схемы, так и совсем простые и далее унифицировано работать с полученными сущностями.
    Таким образом, в случае объектов с композицией многих других объектов, имеющих глубокую иерархию (притом что имеется поддержка глубокого копирования) – мы получаем главное преимущество: код конструирования таких объектов – не зависит от глубины иерархии отдельных составляющих его элементов-сущностей, все это делается одной операцией, одинаково для всех объектов с любой композицией вложенных элементов.
  4. Уменьшение числа подклассов.
    Паттерн фабричный метод часто порождает иерархию классов Creator, параллельную иерархии классов продуктов. Прототип позволяет клонировать прототип, а не запрашивать фабричный метод создать новый объект. Поэтому иерархия класса Creator становится вообще ненужной.
  5. Динамическое конфигурирование приложения классами.
    Некоторые среды позволяют динамически загружать классы в приложение во время его выполнения. Паттерн прототип - это ключ к применению таких возможностей в таком языке.
 
Основной недостаток паттерна прототип заключается в том, что каждый подкласс класса Prototype должен реализовывать операцию Clone, а это далеко не всегда просто. Например, сложно добавить операцию Clone, когда рассматриваемые классы уже существуют. Проблемы возникают и в случае, если во внутреннем представлении объекта есть другие объекты или наличествуют круговые ссылки.

Пример

Здесь имеем очень интересную возможность осуществить оптимизацию программы создания авто-парка. Вспомним, что там для создания нового семейства машин автопарка создается новая конкретная фабрика, переопределяющая фабричные методы создания отдельных видов автомобилей. Применив паттерн прототип – мы совсем избавляется от дополнительного создания подклассов для каждого нового семейства.
Итак, вашему вниманию предлагается следующая версия фабрики авто-парка: PrototypeFleetFactory.
 
Конструктору просто передаем прототипы легкового автомобиля, автобуса и грузовика и затем в функциях добавления – просто клонируем их, устанавливая новый номер.
Теперь для создания авто-парка с другим семейством автомобилей, клиенту вовсе не нужно инстанцировать новый подкласс фабрики парка, вместо этого он снова использует PrototypeFleetFactory, лишь передавая туда другие объекты-продукты относящиеся уже к другому семейству: PrototypeFleetRun.
Обратите внимание на замещение метода GetFactory: при переходе от одного семейства машин к другому теперь нужно лишь передать конструктору другие объекты автомобилей. Номер при этом не важен – он далее сам заполняется в методе CreateFleet(). +дабы не дублировать много кода – просто наследуемся он предыдущей реализации клиента по создания парка (FleetRun). При этом остается лишь переопределить один бизнес-метод – GetFactory (метод main не переопределяется, это скорее системный метод, не относящийся к бизнес-логике – в нашем случае можно так сказать), работа остальных наследуется и не претерпевает каких-либо изменений, т.к. в них везде используется интерфейс абстрактной фабрики (FleetFactory), к которой принадлежит и PrototypeFleetFactory в том числе.
 

Известные применения паттерна Прототип (Prototype)

Быть может, впервые паттерн прототип был использован в системе Sketchpad Ивана Сазерленда (Ivan Sutherland). Первым широко известным применением этого паттерна в объектно-ориентированном языке была система ThingLab, в которой пользователи могли сформировать составной объект, а затем превратить его в прототип, поместив в библиотеку повторно используемых объектов. Ад ель Голдберг и Давид Робсон упоминают прототипы в качестве паттернов в работе, но Джеймс Коплиен рассматривает этот вопрос гораздо шире. Он описывает связанные с прототипом идиомы языка C++ и приводит много примеров и вариантов.
Etgdb - это оболочка отладчиков на базе ЕТ++, где имеется интерфейс вида point-and-click (укажи и щелкни) для различных командных отладчиков. Для каждого из них есть свой подкласс DebuggerAdaptor. Например, GdbAdaptor настраивает etgdb на синтаксис команд GNU gdb, a SunDbxAdaptor - на отладчик dbx компании Sun. Набор подклассов DebuggerAdaptor не «зашит» в etgdb. Вместо этого он получает имя адаптера из переменной среды, ищет в глобальной таблице прототип с указанным именем, а затем его клонирует. Добавить к etgdb новые отладчики можно, связав ядро с подклассом DebuggerAdaptor, разработанным для этого отладчика.
Библиотека приемов взаимодействия в программе Mode Composer хранит прототипы объектов, поддерживающих различные способы интерактивных отношений. Любой созданный с помощью Mode Composer способ взаимодействия можно применить в качестве прототипа, если поместить его в библиотеку. Паттерн прототип позволяет программе поддерживать неограниченное число вариантов отношений.
Пример музыкального редактора, обсуждавшийся в начале этого раздела, основан на каркасе графических редакторов Unidraw.

Родственные паттерны

В некоторых отношениях прототип и абстрактная фабрика являются конкурентами. Но их используют и совместно. Абстрактная фабрика может хранить набор прототипов, которые клонируются и возвращают изготовленные объекты.
В тех проектах, где активно применяются паттерны компоновщик и декоратор, тоже можно извлечь пользу из прототипа.

Реализации:

java(2)   +добавить

1) PrototypeFleetFactory.java на java, code #431[автор:this]
2) PrototypeFleetRun.java на java, code #432[автор:this]