Prototype (Прототип)

Паттерн Prototype (Прототип) позволяет копировать объекты, не вдаваясь в подробности их реализации. Он особенно полезен, когда создание нового объекта «с нуля» — дорогостоящая операция, а клонирование существующего быстрее и проще.

Когда применять Prototype

  • Когда создание объекта требует больших затрат ресурсов или времени.
  • Когда нужно избежать зависимости от конкретных классов при копировании объектов.
  • Когда необходимо клонировать сложные структуры объектов (например, графы или древовидные данные).

PlantUML-диаграмма

UML диаграмма Prototype

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 — удобный паттерн для клонирования сложных объектов. Он снижает зависимость от конкретных классов и ускоряет создание новых экземпляров, но требует аккуратной реализации, особенно при глубоких копиях.