В чем смысл шаблона проектирования прототипа?

Итак, я изучаю шаблоны проектирования в школе. Сегодня мне рассказали о шаблоне проектирования «Прототип».

Я, должно быть, чего-то упускаю, потому что не вижу в этом пользы. Я видел в сети, что люди говорят, что это быстрее, чем использовать new, но это не имеет смысла; в какой-то момент, независимо от того, как создается новый объект, для него необходимо выделить память.

Разве этот паттерн не повторяется в тех же кругах, что и проблема «курица или яйцо»? Поскольку шаблон прототипа по сути является просто клонированием объектов, в какой-то момент исходный объект должен быть создан сам (т.е. не клонирован). Это означало бы, что мне нужно иметь существующую копию каждого объекта, который я хочу клонировать, уже готового к клонированию?

Может ли кто-нибудь объяснить, в чем польза этого шаблона?


person user1905391    schedule 14.12.2012    source источник
comment
Этот вопрос может быть лучше подходит для программистов StackExchange, поскольку это скорее концептуальный вопрос о программировании.   -  person WebChemist    schedule 15.12.2012


Ответы (7)


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

Шаблон полезен для удаления кучи шаблонного кода, когда требуемая конфигурация была бы обременительной. Я думаю о прототипах как о предустановленном объекте, в котором вы сохраняете набор состояний в качестве новой отправной точки.

person Mark Pauley    schedule 14.12.2012
comment
«утиная типизация и множественное наследование на платформах, которые изначально не поддерживают такие вещи». Хорошая точка зрения. Но все же остается досадная проблема необходимости клонирования исходного объекта. - person user1905391; 15.12.2012
comment
Итак, возвращаясь к исходному вопросу, в чем смысл? Как решить проблему курицы / яйца? Книга GoF описывает идею Prototype Manager, но не дает достаточно конкретных деталей о том, как он может работать (если класс зарегистрирован в диспетчере во время выполнения, то как можно избежать того, чтобы клиенты этого менеджера выполняли явное приведение возвращенный клонированный экземпляр?) - person Adam Parkin; 22.02.2013
comment
@Mark, можешь ли ты решить проблему курицы или яйца, которая, кажется, является сутью вопроса? - person jaco0646; 22.02.2017
comment
Прототип - это клонировать этот объект и внести незначительные изменения. Это правда, что вам еще нужно создать исходный объект. Это не то, что пытается решить этот метод создания, скорее он обращается к случаю, когда у вас есть несколько строго настроенных объектов, которые могут служить отправной точкой. Похож на пресет для стерео или синтезатора. - person Mark Pauley; 28.02.2017
comment
Кроме того, существуют языки (Javascript), в которых, строго говоря, нет другого способа сделать то, что мы обычно считаем объектом, то есть новый / отдельный экземпляр инкапсулированных данных, связанных с аксессорами или методами. - person Mark Pauley; 28.02.2017
comment
Только это дало мне понять, для чего на самом деле используется Prototype Pattern. - person NuttLoose; 07.02.2018
comment
У меня есть пример, в котором это может быть полезно. Представьте, что инициализация объекта несложная, но дорогостоящая, например, инициализация может занять 3 минуты. Состояние объекта после инициализации невелико. Клонирование этого объекта может помочь сократить время, необходимое для инициализации других объектов. Это очень специфический случай, и у меня нет реального примера для него, но я думаю, что это вполне может произойти. - person AFP_555; 02.06.2021

Паттерн-прототип имеет некоторые преимущества, например:

  • Это устраняет (потенциально дорогие) накладные расходы на инициализацию объекта.
  • Это упрощает и может оптимизировать вариант использования, когда несколько объектов одного типа будут иметь в основном одни и те же данные.

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

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

person marcus erronius    schedule 15.12.2012
comment
Придирка (я поставил +1), но я думаю, что пункт №1 сформулирован несколько жестко. Это потенциально снижает накладные расходы на инициализацию объекта. Во многих случаях невозможно избежать сложного кода инициализации, и в этом случае шаблон ничего вам не даст. Я также не хотел бы поощрять идею о том, что клонированный объект может обмениваться данными с другими клонами. Типичная идея состоит в том, что клоны должны быть независимыми, поэтому, если вы не будете осторожны, вы можете легко разорвать эти отношения, если поделитесь (не копируете) состояние между клонами. - person Adam Parkin; 22.02.2013
comment
Хороший ответ! Только ваш второй вариант использования нарушает соглашение, предложенное в Java API (по соглашению объект, возвращаемый этим методом, не должен зависеть от этого объекта (который клонируется)., См. docs.oracle.com/javase/7/docs/api/java/lang/Object. html # clone ()) - person robert; 10.02.2016
comment
Второй момент решается с помощью паттерна наилегчайшего веса, а не прототипа. - person AFP_555; 02.06.2021

Многие другие ответы здесь говорят об экономии затрат на клонирование уже настроенного объекта, но я хотел бы остановиться на другой «точке» шаблона прототипа. В некоторых языках, где классы рассматриваются как объекты первого класса, вы можете настроить тип объекта, создаваемого клиентом во время выполнения, просто передав ему имя класса. В таких языках, как C ++, где классы не рассматриваются как объекты первого класса, шаблон «Прототип» позволяет добиться того же эффекта.

Например, предположим, что у нас есть Chef в ресторане, чья работа заключается в приготовлении и подаче блюд. Допустим, Chef недоплачивают и недовольны, поэтому он готовит такие блюда:

class Chef {
    public:
        void prepareMeal() const {
            MozzarellaSticksWithKetchup* appetizer = new MozzarellaSticksWithKetchup();
            // do something with appetizer...

            HockeyPuckHamburgerWithSoggyFries* entree = new HockeyPuckHamburgerWithSoggyFries();
            // do something with entree...

            FreezerBurnedIceCream* dessert = new FreezerBurnedIceCream();
            // do something with dessert...
        }
};

Теперь предположим, что мы хотим изменить Chef на показного знаменитого шеф-повара. Это означает, что он / она должны new разные блюда в prepareMeal(). Мы хотели бы изменить метод, чтобы типы блюд, которые получает new от Chef, можно было указывать в качестве параметров. В других языках, где классы являются объектами первого класса, мы можем просто передать имена классов в качестве параметров методу. Мы не можем сделать это в C ++, поэтому мы можем извлечь выгоду из шаблона прототипа:

class Appetizer {
    public:
        virtual Appetizer* clone() const = 0;
        // ...
};

class Entree {
    public:
        virtual Entree* clone() const = 0;
        // ...
};

class Dessert {
    public:
        virtual Dessert* clone() const = 0;
        // ...
};

class MozzarellaSticksWithKetchup : public Appetizer {
    public:
        virtual Appetizer* clone() const override { return new MozzarellaSticksWithKetchup(*this); }
        // ...
};

class HockeyPuckHamburgerWithSoggyFries : public Entree {
    public:
        virtual Entree * clone() const override { return new HockeyPuckHamburgerWithSoggyFries(*this); }
        // ...
};

class FreezerBurnedIceCream : public Dessert {
    public:
        virtual Dessert * clone() const override { return new FreezerBurnedIceCream(*this); }
        // ...
};

// ...and so on for any other derived Appetizers, Entrees, and Desserts.

class Chef {
    public:
        void prepareMeal(Appetizer* appetizer_prototype, Entree* entree_prototype, Dessert* dessert_prototype) const {
            Appetizer* appetizer = appetizer_prototype->clone();
            // do something with appetizer...

            Entree* entree = entree_prototype->clone();
            // do something with entree...

            Dessert* dessert = dessert_prototype->clone();
            // do something with dessert...
        }
};

Обратите внимание, что метод clone() создает экземпляр производного типа, но возвращает указатель на родительский тип. Это означает, что мы можем изменить тип создаваемого объекта, используя другой производный тип, и клиент не заметит разницы. Этот дизайн теперь позволяет нам настроить Chef - клиент наших прототипов - для приготовления различных типов блюд во время выполнения:

Chef chef;

// The same underpaid chef from before:
MozzarellaSticksWithKetchup mozzarella_sticks;
HockeyPuckHamburgerWithSoggyFries hamburger;
FreezerBurnedIceCream ice_cream;
chef.prepareMeal(&mozzarella_sticks, &hamburger, &ice_cream);

// An ostentatious celebrity chef:
IranianBelugaCaviar caviar;
LobsterFrittataWithFarmFreshChives lobster;
GoldDustedChocolateCupcake cupcake;
chef.prepareMeal(&caviar, &lobster, &cupcake);

Вы можете задаться вопросом, что при использовании этого способа шаблон Prototype дает вам то же самое, что и шаблон Factory Method, так почему бы просто не использовать его? Поскольку шаблон Factory Method потребует иерархии классов-создателей, которые отражают иерархию создаваемых продуктов; то есть нам понадобится MozzarellaSticksWithKetchupCreator с make() методом, HockeyPuckHamburgerWithSoggyFriesCreator с make() методом и так далее. Таким образом, вы можете рассматривать шаблон прототипа просто как один из способов уменьшить избыточность кода, часто вносимую шаблоном Factory Method.

Этот аргумент взят из книги Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования, также известной как «Банда четырех».

person Ethan Hearne    schedule 21.06.2018

Если вы хотите создать объект, но не хотите выполнять дорогостоящую процедуру создания объекта, в которой выполняются вызовы сети или базы данных, используйте шаблон прототипа. Просто создайте копию объекта и внесите в нее изменения.

person user2328970    schedule 14.10.2016

По сравнению с абстрактным шаблоном фабрики, при использовании шаблона прототипа вам не нужно иметь большую иерархию фабрик, только большую иерархию продуктов.

person Michael Zheng    schedule 15.04.2018

Если у вас есть требование, где вам нужно заполнить или использовать те же данные, которые содержат повторяемый объект

и

невозможно построить из существующего объекта, например [Строительство объекта с использованием сетевого потока] или

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

person Andy    schedule 08.04.2015

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

person davidXman    schedule 23.06.2021