Прототип, (англ. Prototype) — порождающий шаблон проектирования.
Прототип | |
---|---|
Prototype | |
![]() | |
Тип | порождающий |
Описан в (Design Patterns) | Да |
Назначение
Задаёт виды создаваемых объектов с помощью экземпляра-прототипа и создаёт новые объекты путём копирования этого прототипа. Он позволяет уйти от реализации и позволяет следовать принципу «программирование через интерфейсы». В качестве возвращающего типа указывается интерфейс/абстрактный класс на вершине иерархии, а классы-наследники могут подставить туда наследника, реализующего этот тип.
Проще говоря, это паттерн создания объекта через клонирование другого объекта вместо создания через конструктор.
Применение
Паттерн используется чтобы:
- избежать дополнительных усилий по созданию объекта стандартным путём (имеется в виду использование конструктора, так как в этом случае также будут вызваны конструкторы всей иерархии предков объекта), когда это непозволительно дорого для приложения.
- избежать наследования создателя объекта (object creator) в клиентском приложении, как это делает паттерн (abstract factory).
Используйте этот шаблон проектирования, когда системe безразлично как именно в ней создаются, компонуются и представляются продукты:
- инстанцируемые классы определяются во время выполнения, например с помощью динамической загрузки;
- избежать построения иерархий классов или фабрик, параллельных иерархии классов продуктов;
- экземпляры класса могут находиться в одном из нескольких различных состояний. Может оказаться удобнее установить соответствующее число прототипов и клонировать их, а не инстанцировать каждый раз класс вручную в подходящем состоянии.
![image](https://www.wikidata.ru-ru.nina.az/image/aHR0cHM6Ly93d3cud2lraWRhdGEucnUtcnUubmluYS5hei9pbWFnZS9hSFIwY0hNNkx5OTFjR3h2WVdRdWQybHJhVzFsWkdsaExtOXlaeTkzYVd0cGNHVmthV0V2Y25Vdk1pOHlOUzlRY205MGIzUjVjR1V1WjJsbS5naWY=.gif)
Примеры
Пример на Python
#!/usr/bin/env python # -*- coding: utf-8 -*- import copy class Prototype: def __init__(self): self._objects = {} def register_object(self, name, obj): """Register an object""" self._objects[name] = obj def unregister_object(self, name): """Unregister an object""" del self._objects[name] def clone(self, name, **attr): """Clone a registered object and update inner attributes dictionary""" obj = copy.deepcopy(self._objects.get(name)) obj.__dict__.update(attr) return obj class A: def __init__(self): self.x = 3 self.y = 8 self.z = 15 self.garbage = [38, 11, 19] def __str__(self): return '{} {} {} {}'.format(self.x, self.y, self.z, self.garbage) def main(): a = A() prototype = Prototype() prototype.register_object('objecta', a) b = prototype.clone('objecta') c = prototype.clone('objecta', x=1, y=2, garbage=[88, 1]) print([str(i) for i in (a, b, c)]) if __name__ == '__main__': main()
- OUTPUT ###
- ['3 8 15 [38, 11, 19]', '3 8 15 [38, 11, 19]', '1 2 15 [88, 1]']
Пример на C++
class Meal { public: virtual ~Meal(); virtual void eat() = 0; virtual Meal *clone() const = 0; //... }; class Spaghetti : public Meal { public: Spaghetti( const Spaghetti &); void eat(); Spaghetti *clone() const { return new Spaghetti( *this ); } //... };
Пример на Java
/** * Prototype Class */ public class Cookie implements Cloneable { protected int weight; @Override public Cookie clone() throws CloneNotSupportedException { Cookie copy = (Cookie) super.clone(); //In an actual implementation of this pattern you might now change references to //the expensive to produce parts from the copies that are held inside the prototype. return copy; } } /** * Concrete Prototypes to clone */ public class CoconutCookie extends Cookie { }
/** * Client Class */ public class CookieMachine { private Cookie cookie; // Could have been a private Cloneable cookie. public CookieMachine(Cookie cookie) { this.cookie = cookie; } public Cookie makeCookie() throws CloneNotSupportedException { return (Cookie) this.cookie.clone(); } public static void main(String args[]) throws CloneNotSupportedException { Cookie tempCookie = null; Cookie prot = new CoconutCookie(); CookieMachine cm = new CookieMachine(prot); for (int i = 0; i < 100; i++) tempCookie = cm.makeCookie(); } }
Пример на Scala
package com package object prototype { class Waffle( protected var name: String, protected var primaryFilling: String, protected var specialFilling: Option[String] = None ) extends Cloneable { override def clone(): Waffle = { super.clone().asInstanceOf[Waffle] } def output() : Unit = { println(s"Waffle $name with primary filling $primaryFilling" + (if (specialFilling != None) specialFilling.get else "")) } } object PrototypeTest { def main(args: Array[String]) : Unit = { println("Output:") val chocolateWaffle = new Waffle("ChocolateWaffle", "Chocolate") chocolateWaffle.output() chocolateWaffle.clone().output() val coconutWaffle = new Waffle("CoconutWaffle","Condensed milk", Some("Coconut")) coconutWaffle.output() coconutWaffle.clone().output() } } } // Output: // Waffle ChocolateWaffle with primary filling Chocolate // Waffle ChocolateWaffle with primary filling Chocolate // Waffle CoconutWaffle with primary filling Condensed milkCoconut // Waffle CoconutWaffle with primary filling Condensed milkCoconut
Пример на C#
using System; namespace Prototype { class MainApp { static void Main() { // Create two instances and clone each Prototype prototype1 = new ConcretePrototype1("I"); Prototype clonedPrototype1 = prototype1.Clone(); Console.WriteLine ("Cloned: {0}", clonedPrototype1 .Id); Prototype prototype2 = new ConcretePrototype2("II"); Prototype clonedPrototype2 = prototype2.Clone(); Console.WriteLine ("Cloned: {0}", clonedPrototype2 .Id); } } // "Prototype" public abstract class Prototype { // Constructor public Prototype(string id) { this.Id = id; Console.Write("Base constructor is called."); } // Property public string Id { get; private set; } public virtual Prototype Clone() { // Shallow copy return (Prototype)this.MemberwiseClone(); } } // "ConcretePrototype1" public class ConcretePrototype1 : Prototype { // Constructor public ConcretePrototype1(string id) : base(id) { } } // "ConcretePrototype2" public class ConcretePrototype2 : Prototype { // Constructor public ConcretePrototype2(string id) : base(id) { } } }
Пример на PHP
<?php namespace RefactoringGuru\Prototype\Conceptual; /** * Пример класса, имеющего возможность клонирования. Мы посмотрим как происходит * клонирование значений полей разных типов. */ class Prototype { public $primitive; public $component; public $circularReference; /** * PHP имеет встроенную поддержку клонирования. Вы можете «клонировать» * объект без определения каких-либо специальных методов, при условии, что * его поля имеют примитивные типы. Поля, содержащие объекты, сохраняют свои * ссылки в клонированном объекте. Поэтому в некоторых случаях вам может * понадобиться клонировать также и вложенные объекты. Это можно сделать * специальным методом clone. */ public function __clone() { $this->component = clone $this->component; // Клонирование объекта, который имеет вложенный объект с обратной // ссылкой, требует специального подхода. После завершения клонирования // вложенный объект должен указывать на клонированный объект, а не на // исходный объект. $this->circularReference = clone $this->circularReference; $this->circularReference->prototype = $this; } } class ComponentWithBackReference { public $prototype; /** * Обратите внимание, что конструктор не будет выполнен во время * клонирования. Если у вас сложная логика внутри конструктора, вам может * потребоваться выполнить ее также и в методе clone. */ public function __construct(Prototype $prototype) { $this->prototype = $prototype; } } /** * Клиентский код. */ function clientCode() { $p1 = new Prototype(); $p1->primitive = 245; $p1->component = new \DateTime(); $p1->circularReference = new ComponentWithBackReference($p1); $p2 = clone $p1; if ($p1->primitive === $p2->primitive) { echo "Primitive field values have been carried over to a clone. Yay!\n"; } else { echo "Primitive field values have not been copied. Booo!\n"; } if ($p1->component === $p2->component) { echo "Simple component has not been cloned. Booo!\n"; } else { echo "Simple component has been cloned. Yay!\n"; } if ($p1->circularReference === $p2->circularReference) { echo "Component with back reference has not been cloned. Booo!\n"; } else { echo "Component with back reference has been cloned. Yay!\n"; } if ($p1->circularReference->prototype === $p2->circularReference->prototype) { echo "Component with back reference is linked to original object. Booo!\n"; } else { echo "Component with back reference is linked to the clone. Yay!\n"; } } clientCode();
Пример на Ruby
module Prototype # "Prototype" class Prototype # Property # свойство id изначально присутствует у каждого объекта, поэтому воспользуемся свойством name attr_reader :name # Constructor def initialize name @name = name end end end # Create an instance and clone it p1 = Prototype::Prototype.new "my name" # объект класса Prototype создается традиционным путём - методом new p2 = p1.clone # метод clone существует у каждого объекта изначально - его не нужно определять puts "p1.id = #{p1.object_id}, p2.id = #{p2.object_id}" # будут напечатаны разные id puts "p1.name = #{p1.name}, p2.name = #{p2.name}" # будут напечатаны одинаковые name - "my name" # Wait for user gets
Пример на VB.NET
Namespace Prototype Class MainApp Shared Sub Main() ' Создание двух экземпляров и клонирование каждого Dim p1 As Prototype = New ConcretePrototype1("I") Dim c1 As Prototype = p1.Clone() Console.WriteLine("Cloned: {0}", c1.Id) Dim p2 As Prototype = New ConcretePrototype2("II") Dim c2 As Prototype = p2.Clone() Console.WriteLine("Cloned: {0}", c2.Id) Console.Read() End Sub End Class ' "Prototype" MustInherit Class Prototype Private m_id As String ' Конструктор Public Sub New(ByVal id As String) Me.m_id = id End Sub ' Свойство Public ReadOnly Property Id() As String Get Return m_id End Get End Property Public MustOverride Function Clone() As Prototype End Class ' "ConcretePrototype1" Class ConcretePrototype1 Inherits Prototype ' Конструктор Public Sub New(ByVal id As String) MyBase.New(id) End Sub Public Overrides Function Clone() As Prototype ' Неполная копия Return DirectCast(Me.MemberwiseClone(), Prototype) End Function End Class ' "ConcretePrototype2" Class ConcretePrototype2 Inherits Prototype ' Конструктор Public Sub New(ByVal id As String) MyBase.New(id) End Sub Public Overrides Function Clone() As Prototype ' Неполная копия Return DirectCast(Me.MemberwiseClone(), Prototype) End Function End Class End Namespace
Пример на Delphi
program PrototypePattern; {$APPTYPE CONSOLE} uses SysUtils; type TPrototype = class public function Clone: TPrototype; virtual; abstract; end; type TPrototypeType = class(TPrototype) private FID: Integer; FInfo: String; public property ID: Integer read FID write FID; property Info: String read FInfo write FInfo; function Clone: TPrototype; override; end; function TPrototypeType.Clone: TPrototype; var vClone: TPrototypeType; begin vClone := TPrototypeType.Create; vClone.ID := ID; vClone.Info := Info; Result := vClone; end; procedure CloneAndShow(Prototype: TPrototypeType); var vClone: TPrototypeType; begin vClone := Prototype.Clone; try Write(vClone.ID); Write(vClone.Info); finally vClone.Free; end; WriteLn; end; var vConcretePrototype1, vConcretePrototype2: TPrototypeType; begin vConcretePrototype1 := TPrototypeType.Create; vConcretePrototype2 := TPrototypeType.Create; try vConcretePrototype1.ID := 10; vConcretePrototype1.Info := ' Prototype1!'; vConcretePrototype2.ID := 11; vConcretePrototype2.Info := ' Prototype2!'; CloneAndShow(vConcretePrototype1); CloneAndShow(vConcretePrototype2); finally vConcretePrototype1.Free; vConcretePrototype2.Free; end; ReadLn; end.
Пример на CoffeeScript
class PresidentPrototype constructor: (@proto) -> clone: -> customer = new President() customer.first = @proto.first customer.last = @proto.last customer.aka = @proto.aka customer class President constructor: (@first, @last, @aka) -> say: -> console.log "His name is #{@first} #{@last} aka #{@aka}." run = -> proto = new President("Jimmy", "Wales", "Jimbo") prototype = new PresidentPrototype(proto) customer = prototype.clone() customer.say() run()
Пример на Io
Foo := Object clone Foo smth := 2 Bar := Foo clone
Литература
- Э. Гамма, Р. Хелм, Р. Джонсон, (Дж. Влиссидес). Приемы объектно-ориентированного проектирования. Паттерны проектирования = Design Patterns: Elements of Reusable Object-Oriented Software. — СПб.: «Питер», 2007. — С. 366. — . (также )
См. также
- (Прототипное программирование)
Примечания
- Прототип на PHP . refactoring.guru. Дата обращения: 18 июня 2023. 18 июня 2023 года.
Ссылки
- Паттерн Prototype (Прототип) — назначение, описание, два варианта реализации (C++), достоинства и недостатки
Википедия, чтение, книга, библиотека, поиск, нажмите, истории, книги, статьи, wikipedia, учить, информация, история, скачать, скачать бесплатно, mp3, видео, mp4, 3gp, jpg, jpeg, gif, png, картинка, музыка, песня, фильм, игра, игры, мобильный, телефон, Android, iOS, apple, мобильный телефон, Samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Сеть, компьютер