프로토타입과 객체 풀
객체를 복제하는 프로토타입과 재사용 가능한 리소스를 효율적으로 관리하는 객체 풀의 사용법을 배웁니다.
프로토타입과 객체 풀은(는) CoddyKit의 무료 Clean Architecture & Design Patterns in Practice 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Clean Architecture & Design Patterns in Practice 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Clean Architecture & Design Patterns in Practice 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Welcome: Prototype & Object Pool
Welcome to this lesson on two powerful Creational Design Patterns: Prototype and Object Pool.
These patterns help us manage object creation efficiently, improving performance and resource utilization in our applications.
Understanding the Prototype Pattern
The Prototype pattern is about creating new objects by cloning an existing instance, rather than creating new ones from scratch. Think of it like a biological clone!
- It's useful when object creation is complex or expensive.
- It avoids the need for subclasses of factories to create objects.
- It allows you to create many similar objects from a 'template' object.
Shallow vs. Deep Copy
When cloning objects, it's crucial to understand shallow copy vs. deep copy:
- Shallow Copy: Creates a new object, but copies references to nested objects. Changes in the clone's nested objects will affect the original.
- Deep Copy: Creates a new object and recursively creates new copies of all nested objects. The clone is completely independent of the original.
Prototype Example: Shallow Copy
In Java, the Cloneable interface and clone() method are used. This example shows a shallow copy, where changing the config in the clone also changes the original.
class Configuration {
String setting;
public Configuration(String setting) {
this.setting = setting;
}
}
class Report implements Cloneable {
String name;
Configuration config;
public Report(String name, Configuration config) {
this.name = name;
this.config = config;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Shallow copy
}
public void display() {
System.out.println("Report: " + name + ", Setting: " + config.setting);
}
}
public class Main {
public static void main(String[] args) {
Configuration initialConfig = new Configuration("Default");
Report originalReport = new Report("Monthly Sales", initialConfig);
try {
Report clonedReport = (Report) originalReport.clone();
clonedReport.name = "Quarterly Sales";
clonedReport.config.setting = "Aggregated"; // Changes original's config!
originalReport.display();
clonedReport.display();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}Prototype Example: Deep Copy
To achieve a deep copy, you must manually clone mutable fields within your clone() method. This ensures the original object's nested state is not affected.
class Configuration implements Cloneable {
String setting;
public Configuration(String setting) {
this.setting = setting;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Report implements Cloneable {
String name;
Configuration config;
public Report(String name, Configuration config) {
this.name = name;
this.config = config;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Report cloned = (Report) super.clone();
cloned.config = (Configuration) config.clone(); // Deep copy for config
return cloned;
}
public void display() {
System.out.println("Report: " + name + ", Setting: " + config.setting);
}
}
public class Main {
public static void main(String[] args) {
Configuration initialConfig = new Configuration("Default");
Report originalReport = new Report("Monthly Sales", initialConfig);
try {
Report clonedReport = (Report) originalReport.clone();
clonedReport.name = "Quarterly Sales";
clonedReport.config.setting = "Aggregated"; // Only changes cloned's config
originalReport.display();
clonedReport.display();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}When to Use Prototype?
Consider the Prototype pattern when:
- Creating objects is resource-intensive (e.g., database queries, heavy computation).
- You need to create many objects that are similar to an existing one.
- You want to decouple your code from the concrete classes of objects you need to create.
- You need to dynamically add or remove objects at runtime.
Introducing the Object Pool Pattern
The Object Pool pattern manages a set of initialized objects that are ready for use. It's like having a ready-to-go toolkit!
Instead of creating and destroying objects frequently, you 'borrow' them from the pool and 'return' them when done. This is especially useful for expensive resources.
How Object Pool Works
The core idea is to reuse objects. An Object Pool typically has:
- Pool: A collection (e.g., a list) of available objects.
- Acquire Method: Retrieves an object from the pool. If none are available, it might create a new one or wait.
- Release Method: Returns an object to the pool, making it available for reuse.
- Initialization: The pool can pre-create a certain number of objects at startup.
Object Pool Example
Here's a simple example of an Object Pool managing DatabaseConnection objects. Notice how connections are acquired and then released back to the pool.
import java.util.ArrayList;
import java.util.List;
class DatabaseConnection {
private int id;
public DatabaseConnection(int id) {
this.id = id;
System.out.println("Connection " + id + " created.");
}
public void connect() {
System.out.println("Connection " + id + " connected.");
}
}
class ConnectionPool {
private List<DatabaseConnection> availableConnections = new ArrayList<>();
private List<DatabaseConnection> inUseConnections = new ArrayList<>();
private int maxPoolSize;
private int counter = 0;
public ConnectionPool(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public synchronized DatabaseConnection acquireConnection() {
if (availableConnections.isEmpty() && inUseConnections.size() < maxPoolSize) {
DatabaseConnection newConn = new DatabaseConnection(++counter);
inUseConnections.add(newConn);
return newConn;
} else if (!availableConnections.isEmpty()) {
DatabaseConnection conn = availableConnections.remove(0);
inUseConnections.add(conn);
return conn;
} else {
System.out.println("Pool is full, no connections available.");
return null; // Or wait, throw exception
}
}
public synchronized void releaseConnection(DatabaseConnection connection) {
if (connection != null && inUseConnections.remove(connection)) {
availableConnections.add(connection);
System.out.println("Connection " + connection.id + " released.");
} else {
System.out.println("Attempted to release unknown connection.");
}
}
}
public class Main {
public static void main(String[] args) {
ConnectionPool pool = new ConnectionPool(2);
DatabaseConnection conn1 = pool.acquireConnection();
if (conn1 != null) conn1.connect();
DatabaseConnection conn2 = pool.acquireConnection();
if (conn2 != null) conn2.connect();
DatabaseConnection conn3 = pool.acquireConnection(); // Should say pool is full
pool.releaseConnection(conn1);
DatabaseConnection conn4 = pool.acquireConnection(); // Should reuse conn1
if (conn4 != null) conn4.connect();
}
}Benefits of Object Pool
Using an Object Pool offers significant advantages:
- Performance: Reduces the overhead of object creation and garbage collection.
- Resource Management: Controls the maximum number of active objects, preventing resource exhaustion.
- Reduced Latency: Objects are ready immediately, without the delay of instantiation.
- Improved Stability: Predictable resource usage.
Quick Check: Design Patterns
You have a complex ImageProcessor object that takes a long time to initialize due to loading large configuration files. You need to create many instances of this processor, each with slightly different parameters, but based on the same initial setup. Which design pattern would be most suitable for efficiently creating these new ImageProcessor instances?
Recap: Prototype & Object Pool
Great work! In this lesson, you learned about two powerful creational patterns:
- Prototype: Creates new objects by cloning existing ones, useful for expensive or complex object instantiation. Remember the difference between shallow and deep copies!
- Object Pool: Manages a collection of reusable objects, reducing the overhead of creating and destroying costly resources like database connections.
These patterns are key to building performant and resource-efficient applications!
자주 묻는 질문
“프로토타입과 객체 풀” 강의는 무료인가요?
네 — “프로토타입과 객체 풀” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Clean Architecture & Design Patterns in Practice 강의 전체를 잠금 해제할 수 있습니다. Clean Architecture & Design Patterns in Practice 강의에는 총 4개의 강의가 포함되어 있습니다.
“프로토타입과 객체 풀”에서 뭘 배우나요?
객체를 복제하는 프로토타입과 재사용 가능한 리소스를 효율적으로 관리하는 객체 풀의 사용법을 배웁니다. 브라우저에서 직접 실행하는 실습 코드로 Clean Architecture & Design Patterns in Practice을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Clean Architecture & Design Patterns in Practice을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Clean Architecture & Design Patterns in Practice은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“프로토타입과 객체 풀” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Clean Architecture & Design Patterns in Practice 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Clean Architecture & Design Patterns in Practice 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 싱글턴과 팩토리 메서드
- 추상 팩토리와 빌더
- 프로토타입과 객체 풀
- 생성 기법으로서의 의존성 주입