Творчі патерни: Прототип

pic

У цій статті ми з вами розглянемо патерни проєктування в Java — їх значення, важливість і застосування в реальних проектах. Не хвилюйтеся, ми будемо поступово розглядати все і тримати пояснення простими.

Патерни проєктування — це ефективні рішення для часто зустрічаються проблем в програмуванні. Вони допомагають створювати чистий, зручний для підтримки код і роблять командну роботу простішою завдяки спільній мові розробників.

Ми розглянемо три основні типи патернів: Творчі (Creational), Структурні (Structural) та Поведенкові (Behavioral). Кожен тип буде розглянуто через приклади з детальними поясненнями.

Якщо ви вже знайомі з Java і розумієте основи об’єктно-орієнтованого програмування, то можете сміливо почати. Поїхали!

pic

Фото: Amélie Mourichon на Unsplash

Прототип — це творчий патерн проєктування, який дозволяє копіювати існуючі об’єкти без залежності вашого коду від їх класів.

Проблема

Уявіть, що ви розробляєте додаток, в якому потрібно створити кілька екземплярів складного об’єкта, наприклад, Документ, що містить різноманітні властивості: текст, зображення та форматування. Кожен раз створювати такий екземпляр з нуля може бути надто ресурсоємно. Ви хочете уникнути витрат на створення нового об’єкта, копіюючи вже існуючий екземпляр і вносячи незначні зміни.

Рішення

Патерн Прототип допомагає вирішити цю проблему, дозволяючи створювати нові об’єкти шляхом копіювання існуючих. Це особливо корисно, коли створення об’єктів передбачає дорогі операції, як-то введення/виведення (I/O), виклики до баз даних або складні ініціалізації.

Структура

  1. Інтерфейс Прототип: Оголошує метод для клонування самого себе.
  2. Конкретний Прототип: Реалізує інтерфейс Прототип і надає логіку клонування.
  3. Клієнт: Використовує інтерфейс Прототип для клонування об’єктів, замість того, щоб створювати їх вручну.

Застосування

Цей патерн корисний, коли:

  • Створення нових екземплярів класу є занадто затратним.
  • Потрібно уникнути витрат на створення об’єктів з нуля.
  • Необхідно створювати багато об’єктів із схожим станом, але з незначними відмінностями.

Це особливо корисно, коли об’єкти складно створити або вони мають багато налаштувань.

Як реалізувати

  1. Визначте Інтерфейс Прототип: Створіть інтерфейс, який містить метод для клонування об’єкта.
public interface Prototype {  
 Prototype clone();  
}
  1. Створіть Конкретний Прототип: Реалізуйте інтерфейс і визначте, як здійснюється клонування. Ви можете вибрати між поверхневим копіюванням або глибоким копіюванням, залежно від складності об’єкта.
import java.util.Date;  

public class Document implements Prototype {  
 private String text;  
 private Date creationDate;  

 public Document(String text, Date creationDate) {  
 this.text = text;  
 this.creationDate = creationDate;  
 }  

 @Override  
 public Prototype clone() {   
 return new Document(this.text, this.creationDate);  
 }  

 public void setText(String text) {  
 this.text = text;  
 }  

 public String getText() {  
 return text;  
 }  

 public Date getCreationDate() {  
 return creationDate;  
 }  

 @Override  
 public String toString() {  
 return "Document{" +  
 "text='" + text + '\'' +  
 ", creationDate=" + creationDate +  
 '}';  
 }  
}

Якщо клас містить змінні посилання (наприклад, Date), для безпеки використовуйте глибоке копіювання: new Date(this.creationDate.getTime())

  1. Використання Прототипа: Клієнтський код використовує метод clone() для створення копії об’єкта замість того, щоб створювати новий вручну.
public class PrototypeDemo {  
 public static void main(String[] args) {  
   Document original = new Document("Original text", new Date());  
   Document cloned = (Document) original.clone();  

   System.out.println(original == cloned); // Виведе: false  
   System.out.println(original.getText());  
   System.out.println(cloned.getText());  
 }  
}

Використовуємо Інтерфейс Прототип для Клонування Об’єктів: клієнтський код клонує об’єкт замість створення нового з нуля.

public class PrototypeApp {  
 public static void main(String[] args) {  
 Document originalDocument = new Document("Original Text", new Date());  
 System.out.println("Original Document: " + originalDocument);  

 Document clonedDocument = (Document) originalDocument.clone();  
 clonedDocument.setText("Cloned Text");  

 System.out.println("Cloned Document: " + clonedDocument);  
 }  
}

Чи використовує це Spring Framework, і якщо так, то як?

Так, Spring активно підтримує патерн Прототип через свої обсяги бінів.

За замовчуванням Spring використовує синглтон-обсяг для бінів. Однак, якщо бін має обсяг прототипа, Spring створює новий екземпляр щоразу, коли цей бін запитується з контейнера.

Ось як можна визначити бін з обсягом прототипа:

import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.Scope;  

@Configuration  
public class AppConfig {  

 @Bean  
 @Scope("prototype")  
 public Document document() {  
 return new Document("Prototype Text", new Date());  
 }  
}

Кожного разу, коли цей бін інжектується або запитується, Spring повертає новий екземпляр:

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  

@Component  
public class DocumentService {  
 @Autowired  
 private Document document;  

 public void printDocument() {  
 System.out.println(document);  
 }  
}

️ Spring керує лише створенням прототипів. Життєві цикли, такі як @PreDestroy, не будуть автоматично викликані для бінів з обсягом прототипа.

Переваги та недоліки

Переваги:

  • Ефективність: Уникає дорогого створення об’єктів, покращуючи продуктивність.
  • Гнучкість: Клоновані об’єкти можна змінювати незалежно.
  • Спрощене створення: Централізує логіку створення об’єктів через клонування.

Недоліки:

  • Складність: Потребує уважного підходу, особливо для глибокого копіювання.
  • Виклики глибокого копіювання: Забезпечення повного дублювання складних об’єктів може бути важким.
  • Ризики мутабельності: Поверхневе копіювання може призвести до спільного використання змінних полів, що може викликати помилки.
  • Можливість неправильного використання: Невірне застосування може призвести до проблем з відслідковуванням спільних посилань.

Використовуючи патерн Прототип, ви зможете ефективно створювати варіації складних об’єктів, зберігаючи чистоту і повторне використання механізму створення. Цей підхід надзвичайно потужний, коли його застосовують разом з обсягом прототипа в Spring або при створенні фреймворків, що вимагають клонування об’єктів для налаштування або шаблонної логіки.

Github репозиторій: https://github.com/LuisSalas94/design_patterns

Дуже дякую, що прочитали мою статтю до кінця! Сподіваюся, що вона була корисною і ви отримали нові цінні відомості.
Якщо вам сподобалося, будь ласка, поділіться статтею з друзями та колегами — я буду дуже вдячний!

Також, якщо ви хочете глибше зануритися в ці теми, не соромтеся підписатися на мій канал на YouTube для більше підручників і порад.

Ще раз дякую за вашу підтримку!

pic

💖 Скажіть мені 👋!

Фернандо Салас

YouTube: youtube.com/@CodeWithFernando?sub_confirmation=1

Email: [email protected]

LinkedIn: https://www.linkedin.com/in/luisfernandosalasgave/

GitHub: https://github.com/LuisSalas94

Medium: https://medium.com/@luisfernandosalasg

Перегляньте інші мої статті:

Перекладено з: Creational Patterns: Prototype