0Pricing
Clean Architecture & Design Patterns in Practice · 课时

原型与对象池

学习使用原型克隆对象,并使用对象池高效管理可复用资源。

原型与对象池 是 CoddyKit 上的免费 Clean Architecture & Design Patterns in Practice 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 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!

常见问题解答

「原型与对象池」课时是免费的吗?

是的 — 「原型与对象池」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Clean Architecture & Design Patterns in Practice 课程的其余内容,请升级到 CoddyKit PRO。 Clean Architecture & Design Patterns in Practice 课程共包含 4 节课。

「原型与对象池」这节课中我会学到什么?

学习使用原型克隆对象,并使用对象池高效管理可复用资源。 你通过在浏览器中直接运行的动手代码来练习 Clean Architecture & Design Patterns in Practice,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Clean Architecture & Design Patterns in Practice 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Clean Architecture & Design Patterns in Practice 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「原型与对象池」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Clean Architecture & Design Patterns in Practice 课中编写并运行代码吗?

能。每节 Clean Architecture & Design Patterns in Practice 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 单例与工厂方法
  2. 抽象工厂与建造者
  3. 原型与对象池
  4. 将依赖注入作为创建型技术
← 返回 Clean Architecture & Design Patterns in Practice