Прототип (шаблон проектирования)
Прототип, (англ. Prototype) — порождающий шаблон проектирования.
Прототип | |
---|---|
Prototype | |
Тип | порождающий |
Описан в Design Patterns | Да |
НазначениеПравить
Задаёт виды создаваемых объектов с помощью экземпляра-прототипа и создаёт новые объекты путём копирования этого прототипа. Он позволяет уйти от реализации и позволяет следовать принципу «программирование через интерфейсы». В качестве возвращающего типа указывается интерфейс/абстрактный класс на вершине иерархии, а классы-наследники могут подставить туда наследника, реализующего этот тип.
Проще говоря, это паттерн создания объекта через клонирование другого объекта вместо создания через конструктор.
ПрименениеПравить
Паттерн используется чтобы:
- избежать дополнительных усилий по созданию объекта стандартным путём (имеется в виду использование конструктора, так как в этом случае также будут вызваны конструкторы всей иерархии предков объекта), когда это непозволительно дорого для приложения.
- избежать наследования создателя объекта (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
/**
* Иерархия допустимых классов для создания прототипов
*/
abstract class Terrain {}
abstract class Sea extends Terrain {}
class EarthSea extends Sea {}
class MarsSea extends Sea {}
class VenusSea extends Sea {}
abstract class Plains extends Terrain {}
class EarthPlains extends Plains {}
class MarsPlains extends Plains {}
class VenusPlains extends Plains {}
abstract class Forest extends Terrain {}
class EarthForest extends Forest {}
class MarsForest extends Forest {}
class VenusForest extends Forest {}
/**
* Определение логики фабрики прототипов
*/
class TerrainFactory {
private $sea;
private $forest;
private $plains;
public function __construct( Sea $sea, Plains $plains, Forest $forest ) {
$this->sea = $sea;
$this->plains = $plains;
$this->forest = $forest;
}
function getSea( ) {
return clone $this->sea;
}
function getPlains( ) {
return clone $this->plains;
}
function getForest( ) {
return clone $this->forest;
}
}
/**
* Создание фабрики с заданными параметрами прототипа
*/
$prototypeFactory = new TerrainFactory(
new EarthSea(),
new MarsPlains(),
new VenusForest()
);
/**
* Создание заданных объектов путём клонирования
*/
$sea = $prototypeFactory->getSea();
$plains = $prototypeFactory->getPlains();
$forest = $prototypeFactory->getForest();
Пример на 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)
См. такжеПравить
СсылкиПравить
- Паттерн Prototype (Прототип) — назначение, описание, два варианта реализации (C++), достоинства и недостатки