Паттерн Prototype (Прототип) позволяет копировать объекты, не вдаваясь в подробности их реализации. Он особенно полезен, когда создание нового объекта «с нуля» — дорогостоящая операция, а клонирование существующего быстрее и проще.
Когда применять Prototype
- Когда создание объекта требует больших затрат ресурсов или времени.
- Когда нужно избежать зависимости от конкретных классов при копировании объектов.
- Когда необходимо клонировать сложные структуры объектов (например, графы или древовидные данные).
PlantUML-диаграмма
public abstract class Shape
{
public int X { get; set; }
public int Y { get; set; }
public string Color { get; set; }
public Shape() { }
// Конструктор копирования
public Shape(Shape source)
{
X = source.X;
Y = source.Y;
Color = source.Color;
}
public abstract Shape Clone();
}
public class Circle : Shape
{
public int Radius { get; set; }
public Circle() { }
public Circle(Circle source) : base(source)
{
Radius = source.Radius;
}
public override Shape Clone()
{
return new Circle(this);
}
}
public class Rectangle : Shape
{
public int Width { get; set; }
public int Height { get; set; }
public Rectangle() { }
public Rectangle(Rectangle source) : base(source)
{
Width = source.Width;
Height = source.Height;
}
public override Shape Clone()
{
return new Rectangle(this);
}
}
// Использование
class Program
{
static void Main()
{
var circle = new Circle { X = 10, Y = 20, Radius = 15, Color = "Red" };
var anotherCircle = (Circle)circle.Clone();
var rectangle = new Rectangle { X = 5, Y = 5, Width = 50, Height = 100, Color = "Blue" };
var anotherRectangle = (Rectangle)rectangle.Clone();
Console.WriteLine($"Клон круга: X={anotherCircle.X}, Y={anotherCircle.Y}, Radius={anotherCircle.Radius}, Color={anotherCircle.Color}");
Console.WriteLine($"Клон прямоугольника: X={anotherRectangle.X}, Y={anotherRectangle.Y}, W={anotherRectangle.Width}, H={anotherRectangle.Height}");
}
}
// В Go нет наследования, поэтому «клонируемость» — это интерфейс,
// а копирование значения структуры уже даёт поверхностную копию.
type Shape interface {
Clone() Shape
}
type Base struct {
X, Y int
Color string
}
type Circle struct {
Base
Radius int
}
func (c Circle) Clone() Shape { return c }
type Rectangle struct {
Base
Width, Height int
}
func (r Rectangle) Clone() Shape { return r }
func main() {
circle := Circle{Base: Base{X: 10, Y: 20, Color: "Red"}, Radius: 15}
anotherCircle := circle.Clone().(Circle)
rectangle := Rectangle{Base: Base{X: 5, Y: 5, Color: "Blue"}, Width: 50, Height: 100}
anotherRectangle := rectangle.Clone().(Rectangle)
fmt.Printf("Клон круга: X=%d, Y=%d, Radius=%d, Color=%s\n",
anotherCircle.X, anotherCircle.Y, anotherCircle.Radius, anotherCircle.Color)
fmt.Printf("Клон прямоугольника: X=%d, Y=%d, W=%d, H=%d\n",
anotherRectangle.X, anotherRectangle.Y, anotherRectangle.Width, anotherRectangle.Height)
}
import copy
from abc import ABC, abstractmethod
class Shape(ABC):
def __init__(self, x: int = 0, y: int = 0, color: str = "") -> None:
self.x = x
self.y = y
self.color = color
# В Python копирование уже есть в стандартной библиотеке,
# свой конструктор копирования писать не нужно.
@abstractmethod
def clone(self) -> "Shape": ...
class Circle(Shape):
def __init__(self, x: int, y: int, color: str, radius: int) -> None:
super().__init__(x, y, color)
self.radius = radius
def clone(self) -> "Circle":
return copy.deepcopy(self)
class Rectangle(Shape):
def __init__(self, x: int, y: int, color: str, width: int, height: int) -> None:
super().__init__(x, y, color)
self.width = width
self.height = height
def clone(self) -> "Rectangle":
return copy.deepcopy(self)
circle = Circle(10, 20, "Red", 15)
another_circle = circle.clone()
rectangle = Rectangle(5, 5, "Blue", 50, 100)
another_rectangle = rectangle.clone()
print(f"Клон круга: X={another_circle.x}, Y={another_circle.y}, "
f"Radius={another_circle.radius}, Color={another_circle.color}")
print(f"Клон прямоугольника: X={another_rectangle.x}, Y={another_rectangle.y}, "
f"W={another_rectangle.width}, H={another_rectangle.height}")
abstract class Shape {
x = 0;
y = 0;
color = "";
protected constructor(source?: Shape) {
if (source) {
this.x = source.x;
this.y = source.y;
this.color = source.color;
}
}
abstract clone(): Shape;
}
class Circle extends Shape {
radius = 0;
constructor(source?: Circle) {
super(source);
if (source) this.radius = source.radius;
}
clone(): Shape {
return new Circle(this);
}
}
class Rectangle extends Shape {
width = 0;
height = 0;
constructor(source?: Rectangle) {
super(source);
if (source) {
this.width = source.width;
this.height = source.height;
}
}
clone(): Shape {
return new Rectangle(this);
}
}
const circle = new Circle();
Object.assign(circle, { x: 10, y: 20, radius: 15, color: "Red" });
const anotherCircle = circle.clone() as Circle;
const rectangle = new Rectangle();
Object.assign(rectangle, { x: 5, y: 5, width: 50, height: 100, color: "Blue" });
const anotherRectangle = rectangle.clone() as Rectangle;
console.log(`Клон круга: X=${anotherCircle.x}, Y=${anotherCircle.y}, Radius=${anotherCircle.radius}, Color=${anotherCircle.color}`);
console.log(`Клон прямоугольника: X=${anotherRectangle.x}, Y=${anotherRectangle.y}, W=${anotherRectangle.width}, H=${anotherRectangle.height}`);
abstract class Shape {
int x;
int y;
String color;
Shape() { }
// Конструктор копирования
Shape(Shape source) {
this.x = source.x;
this.y = source.y;
this.color = source.color;
}
abstract Shape clone();
}
class Circle extends Shape {
int radius;
Circle() { }
Circle(Circle source) {
super(source);
this.radius = source.radius;
}
Shape clone() {
return new Circle(this);
}
}
class Rectangle extends Shape {
int width;
int height;
Rectangle() { }
Rectangle(Rectangle source) {
super(source);
this.width = source.width;
this.height = source.height;
}
Shape clone() {
return new Rectangle(this);
}
}
public class Program {
public static void main(String[] args) {
Circle circle = new Circle();
circle.x = 10;
circle.y = 20;
circle.radius = 15;
circle.color = "Red";
Circle anotherCircle = (Circle) circle.clone();
Rectangle rectangle = new Rectangle();
rectangle.x = 5;
rectangle.y = 5;
rectangle.width = 50;
rectangle.height = 100;
rectangle.color = "Blue";
Rectangle anotherRectangle = (Rectangle) rectangle.clone();
System.out.printf("Клон круга: X=%d, Y=%d, Radius=%d, Color=%s%n",
anotherCircle.x, anotherCircle.y, anotherCircle.radius, anotherCircle.color);
System.out.printf("Клон прямоугольника: X=%d, Y=%d, W=%d, H=%d%n",
anotherRectangle.x, anotherRectangle.y, anotherRectangle.width, anotherRectangle.height);
}
}
// data class даёт copy() бесплатно — отдельный Clone писать не нужно.
sealed interface Shape {
val x: Int
val y: Int
val color: String
}
data class Circle(
override val x: Int,
override val y: Int,
override val color: String,
val radius: Int,
) : Shape
data class Rectangle(
override val x: Int,
override val y: Int,
override val color: String,
val width: Int,
val height: Int,
) : Shape
fun main() {
val circle = Circle(x = 10, y = 20, color = "Red", radius = 15)
val anotherCircle = circle.copy()
val rectangle = Rectangle(x = 5, y = 5, color = "Blue", width = 50, height = 100)
val anotherRectangle = rectangle.copy()
println("Клон круга: X=${anotherCircle.x}, Y=${anotherCircle.y}, " +
"Radius=${anotherCircle.radius}, Color=${anotherCircle.color}")
println("Клон прямоугольника: X=${anotherRectangle.x}, Y=${anotherRectangle.y}, " +
"W=${anotherRectangle.width}, H=${anotherRectangle.height}")
}
Плюсы и минусы
| Плюсы | Минусы |
|---|---|
| Позволяет копировать объекты без зависимости от их классов | Сложно реализовать глубокое копирование при наличии вложенных объектов |
| Ускоряет создание объектов с заранее известными состояниями | Необходим контроль за корректным копированием всех полей |
| Упрощает добавление новых типов объектов в систему | Может дублировать данные, если клон не требуется часто |
Заключение
Prototype — удобный паттерн для клонирования сложных объектов. Он снижает зависимость от конкретных классов и ускоряет создание новых экземпляров, но требует аккуратной реализации, особенно при глубоких копиях.