Прототип (шаблон проектирования)
Прототип | |
---|---|
Prototype | |
Тип | порождающий |
Описан в Design Patterns | Да |
Прототип, (англ. Prototype) — порождающий шаблон проектирования.
Назначение
[править | править код]Задаёт виды создаваемых объектов с помощью экземпляра-прототипа и создаёт новые объекты путём копирования этого прототипа. Он позволяет уйти от реализации и позволяет следовать принципу «программирование через интерфейсы». В качестве возвращающего типа указывается интерфейс/абстрактный класс на вершине иерархии, а классы-наследники могут подставить туда наследника, реализующего этот тип.
Проще говоря, это паттерн создания объекта через клонирование другого объекта вместо создания через конструктор.
Применение
[править | править код]Паттерн используется чтобы:
- избежать дополнительных усилий по созданию объекта стандартным путём (имеется в виду использование конструктора, так как в этом случае также будут вызваны конструкторы всей иерархии предков объекта), когда это непозволительно дорого для приложения.
- избежать наследования создателя объекта (object creator) в клиентском приложении, как это делает паттерн abstract factory.
Используйте этот шаблон проектирования, когда системe безразлично как именно в ней создаются, компонуются и представляются продукты:
- инстанцируемые классы определяются во время выполнения, например с помощью динамической загрузки;
- избежать построения иерархий классов или фабрик, параллельных иерархии классов продуктов;
- экземпляры класса могут находиться в одном из нескольких различных состояний. Может оказаться удобнее установить соответствующее число прототипов и клонировать их, а не инстанцировать каждый раз класс вручную в подходящем состоянии.
Примеры
[править | править код]Пример на 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. — ISBN 978-5-469-01136-1. (также ISBN 5-272-00355-1)
См. также
[править | править код]Примечания
[править | править код]- ↑ Прототип на PHP . refactoring.guru. Дата обращения: 18 июня 2023. Архивировано 18 июня 2023 года.
Ссылки
[править | править код]- Паттерн Prototype (Прототип) — назначение, описание, два варианта реализации (C++), достоинства и недостатки