← Назад к вопросам

При обращении к чему Prototype будет создавать новый объект

2.0 Middle🔥 101 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# При обращении к чему Prototype будет создавать новый объект

Это вопрос про то, когда Prototype паттерн срабатывает и создаёт новые объекты. Важно понимать момент, когда происходит клонирование.

Основной момент: clone() вызов

Прототип создаёт новый объект только когда явно вызывается метод clone():

prototype.clone()  // ← ВОТ ЗДЕСЬ создаётся новый объект

Практический пример

Конфигурация

public class AppConfig implements Cloneable {
    private String database;
    private int maxConnections;
    private String logLevel;
    private List<String> modules;
    
    @Override
    public AppConfig clone() {
        try {
            AppConfig cloned = (AppConfig) super.clone();
            // Глубокое копирование для список
            cloned.modules = new ArrayList<>(this.modules);
            return cloned;  // ← НОВЫЙ объект создан здесь
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

// ОБратка к свойствам прототипа - новый объект НЕ создаётся
AppConfig prototype = new AppConfig();
prototype.setDatabase("PostgreSQL");
prototype.setMaxConnections(50);
prototype.setLogLevel("INFO");
prototype.addModule("auth");

// Все эти обращения изменяют ТОТ ЖЕ прототип
System.out.println(prototype.getDatabase());  // "PostgreSQL" - не clone
System.out.println(prototype.getLogLevel());   // "INFO" - не clone

// Только clone() создаёт НОВЫЙ объект
AppConfig config1 = prototype.clone();  // ← Новый объект создан
AppConfig config2 = prototype.clone();  // ← Новый объект создан
AppConfig config3 = prototype.clone();  // ← Новый объект создан

// config1, config2, config3 - разные объекты с одинаковыми данными
config1 != config2;  // true (разные объекты в памяти)
config1.equals(config2);  // true (одинаковые данные)

Фабрика с прототипом

public class ConfigFactory {
    private AppConfig prodPrototype;
    private AppConfig devPrototype;
    
    public ConfigFactory() {
        // Инициализируем прототипы один раз
        initializePrototypes();
    }
    
    private void initializePrototypes() {
        prodPrototype = new AppConfig();
        prodPrototype.setDatabase("prod.db");
        prodPrototype.setMaxConnections(100);
        prodPrototype.setLogLevel("ERROR");
        
        devPrototype = new AppConfig();
        devPrototype.setDatabase("dev.db");
        devPrototype.setMaxConnections(10);
        devPrototype.setLogLevel("DEBUG");
    }
    
    // ← При обращении к этому методу создаётся новый объект
    public AppConfig createProdConfig() {
        return prodPrototype.clone();  // ← НОВЫЙ объект
    }
    
    public AppConfig createDevConfig() {
        return devPrototype.clone();   // ← НОВЫЙ объект
    }
}

// Использование
ConfigFactory factory = new ConfigFactory();

// Каждый вызов создаёт НОВЫЙ объект
AppConfig app1 = factory.createProdConfig();  // Новый объект
AppConfig app2 = factory.createProdConfig();  // Новый объект
AppConfig app3 = factory.createProdConfig();  // Новый объект

app1 != app2;  // true (разные объекты)
app1.getDatabase().equals(app2.getDatabase());  // true (одинаковые данные)

Жизненный цикл Prototype

Шаг 1: Инициализация (один раз)
┌──────────────────────────────────────────────┐
│ Создание прототипа                           │
│ prototype = new AppConfig()                  │
│ prototype.setDatabase("prod")                │
│ prototype.setMaxConnections(100)             │
│ prototype.setLogLevel("ERROR")               │
│                                              │
│ Объект в памяти: 1 экземпляр                │
│ Используется для клонирования                │
└──────────────────────────────────────────────┘

Шаг 2: Клонирование (много раз)
┌──────────────────────────────────────────────┐
│ config1 = prototype.clone()  ← НОВЫЙ объект  │
│ config2 = prototype.clone()  ← НОВЫЙ объект  │
│ config3 = prototype.clone()  ← НОВЫЙ объект  │
│                                              │
│ Объектов в памяти: 4 экземпляра             │
│ (1 прототип + 3 клона)                      │
└──────────────────────────────────────────────┘

Сравнение подходов

Без Prototype (каждый раз новое создание)

public AppConfig createProdConfig() {
    AppConfig config = new AppConfig();  // ← НОВОЕ создание
    config.setDatabase("prod");
    config.setMaxConnections(100);
    config.setLogLevel("ERROR");
    config.addModule("auth");
    config.addModule("db");
    config.addModule("cache");
    // ... ещё 20 операций
    return config;
}

// Каждый вызов - полная инициализация
AppConfig app1 = createProdConfig();  // Медленно
AppConfig app2 = createProdConfig();  // Медленно
AppConfig app3 = createProdConfig();  // Медленно

С Prototype (один раз инициализация)

// Инициализация один раз
private AppConfig prodPrototype = new AppConfig();
prodPrototype.setDatabase("prod");
prodPrototype.setMaxConnections(100);
prodPrototype.setLogLevel("ERROR");
prodPrototype.addModule("auth");
prodPrototype.addModule("db");
prodPrototype.addModule("cache");

// Каждый вызов - просто клонирование
public AppConfig createProdConfig() {
    return prodPrototype.clone();  // Быстро
}

// Использование
AppConfig app1 = createProdConfig();  // Быстро
AppConfig app2 = createProdConfig();  // Быстро
AppConfig app3 = createProdConfig();  // Быстро

Реестр прототипов

public class PrototypeRegistry {
    private Map<String, Object> prototypes = new HashMap<>();
    
    public void registerPrototype(String key, Object prototype) {
        prototypes.put(key, prototype);
    }
    
    // При обращении к этому методу - создаётся НОВЫЙ объект
    public <T> T createObject(String key) {
        T prototype = (T) prototypes.get(key);
        if (prototype == null) {
            throw new IllegalArgumentException("Prototype not found: " + key);
        }
        // Клонируем если это Cloneable
        if (prototype instanceof Cloneable) {
            try {
                return (T) prototype.getClass()
                    .getMethod("clone")
                    .invoke(prototype);  // ← НОВЫЙ объект создан
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return prototype;  // Если не Cloneable - возвращаем сам прототип
    }
}

// Использование
PrototypeRegistry registry = new PrototypeRegistry();

// Регистрируем прототипы (один раз)
AppConfig prodPrototype = new AppConfig();
prodPrototype.setDatabase("prod");
registry.registerPrototype("prod", prodPrototype);

AppConfig devPrototype = new AppConfig();
devPrototype.setDatabase("dev");
registry.registerPrototype("dev", devPrototype);

// Создание объектов (много раз)
AppConfig config1 = registry.createObject("prod");  // ← НОВЫЙ
AppConfig config2 = registry.createObject("prod");  // ← НОВЫЙ
AppConfig config3 = registry.createObject("dev");   // ← НОВЫЙ

config1 != config2;  // true (разные объекты)
config1 != config3;  // true (разные объекты)

Диаграмма создания объектов

Прототип в памяти:
┌─────────────────────────────┐
│  AppConfig (prototype)      │
│  ├─ database: "prod"        │
│  ├─ maxConnections: 100     │
│  └─ logLevel: "ERROR"       │
│                             │
│  Адрес в памяти: 0x1000     │
└─────────────────────────────┘

После clone() #1:
┌─────────────────────────────┐
│  AppConfig (prototype)      │  (остаётся неизменным)
│  ├─ database: "prod"        │
│  ├─ maxConnections: 100     │
│  └─ logLevel: "ERROR"       │
│  Адрес: 0x1000              │
└─────────────────────────────┘

┌─────────────────────────────┐
│  AppConfig (clone #1)       │  ← НОВЫЙ объект
│  ├─ database: "prod"        │
│  ├─ maxConnections: 100     │
│  └─ logLevel: "ERROR"       │
│  Адрес: 0x2000              │
└─────────────────────────────┘

После clone() #2:
┌─────────────────────────────┐
│  AppConfig (clone #2)       │  ← ЕЩЁ НОВЫЙ объект
│  ├─ database: "prod"        │
│  ├─ maxConnections: 100     │
│  └─ logLevel: "ERROR"       │
│  Адрес: 0x3000              │
└─────────────────────────────┘

Важно: Поверхностное vs глубокое копирование

public class ConfigWithList implements Cloneable {
    private List<String> modules;
    
    // ❌ ПЛОХО - поверхностное копирование
    @Override
    public ConfigWithList cloneShallow() {
        try {
            return (ConfigWithList) super.clone();
            // modules указывает на ТОТ ЖЕ объект List!
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
    
    // ✅ ХОРОШО - глубокое копирование
    @Override
    public ConfigWithList clone() {
        try {
            ConfigWithList cloned = (ConfigWithList) super.clone();
            // Создаём НОВЫЙ List с копией данных
            cloned.modules = new ArrayList<>(this.modules);
            return cloned;  // ← Новый объект со своим List
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

// Демонстрация
ConfigWithList prototype = new ConfigWithList();
prototype.addModule("auth");

ConfigWithList clone1 = prototype.clone();  // Новый объект
ConfigWithList clone2 = prototype.clone();  // Новый объект

// Изменение clone1 не влияет на clone2
clone1.addModule("db");

clone1.getModules().size();  // 2 ("auth", "db")
clone2.getModules().size();  // 1 ("auth") - не изменился!

Заключение

Новый объект создаётся при обращении к:

  • prototype.clone() - явное клонирование
  • factory.createObject() - в фабрике, которая вызывает clone()
  • registry.createObject("key") - в реестре, который вызывает clone()

НЕ создаётся новый объект при обращении к:

  • prototype.getProperty() - просто чтение
  • prototype.setProperty() - изменение прототипа
  • Любые другие методы - используется ТОТ ЖЕ объект

Ключевой момент: clone() - единственный способ создать новый объект в Prototype паттерне.

При обращении к чему Prototype будет создавать новый объект | PrepBro