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

Схема обсуждаемого сейчас паттерна несколько проще, чем тех, что были рассмотрены ранее:

  • Prototype — здесь объявляется интерфейс для клонирования;
  • ConcretePrototype — конкретная реализация выбранного прототипа, где описывается содержимое интерфейса;
  • Client — здесь создается новый объект, который будет клонировать себя. В нашем же случае все будет просто выполняться в методе Main класса Program.

А теперь перейдем к конкретному примеру. Представьте, что вы имеете дело с отделом по продаже детских игрушек и продаете только машинки. В базе данных они помечаются тремя параметрами: идентификатором (ID) модели (ему соответствует марка машины), максимальной скоростью, развиваемой игрушкой (кстати, она с радиоуправлением!), и возрастом ребенка, начиная с которого ему допустимо с ней «общаться» (вспомните, как это представлено в конструкторах LEGO). Паттерн Prototype будет использоваться для клонирования одинаковых машинок. Итак, сначала объявим интерфейс (листинг 1).

Схема паттерна Prototype

В комментариях достаточно подробно рассказано, что есть что в классе Auto. Тем не менее следует обратить внимание на метод Clone, предназначенный для клонирования объектов. Здесь он абстрактен. Конкретные реализации метода будут представлены ниже, в соответствующих классах ConcreteAuto1 и ConcreteAuto2 (листинг 2).

Данный фрагмент текста программы требует более подробных пояснений. Мы обращаемся к базовому конструктору, определенному в классе Auto, с помощью двоеточия и указания ключевого слова base после нашего конструктора. При желании, конечно, несложно было бы реализовать и свой конструктор, но здесь делать это абсолютно незачем. Сам метод клонирования также интересен. Реализовать его можно совершенно по-разному, все зависит от сложностей системы, которую вы строите.

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

Теперь рассмотрим, как работать с паттерном Prototype. Создадим обычное консольное приложение с классом Program и статическим методом Main (листинг 3).

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

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


Листинг 1 - 2

Листинг 3