Mẫu thiết kế Object Pool

Creational Design Patterns

Mô tả chung

Việc khởi tạo và hủy đối tượng liên tụclà một việc gây tốn rất nhiều tài nguyên. Ví dụ như trong game bắn máy bay bạn có thể phải tạo hàng ngàn viên đạn trong thời gian ngắn. Để giải quyết vấn đề này ta sử dụng một mẫu thiết kế là objects pool. Objects pool giúp cải thiện hiệu suất và sử dụng bộ nhớ bằng sử dụng lại các đối tượng từ một pool cố định thay vì việc khởi tạo và hủy bỏ chúng.

Objects pool là một lớp dùng để lưu trữ lại các đối tượng. Bạn sẽ khởi tạo tối đa số lượng đối tượng có thể được sử dụng và đưa vào pool. Khi đối tượng được sử dụng xong bạn sẽ không hủy nó mà sẽ đưa nó ra khỏi màn hình hiển thị hoặc ẩn đi. Objects pool được sử dụng khi:

  • Khi bạn cần tạo và hủy đối tượng liên tục
  • Các đối tượng là tương tự nhau
  • Các đối tượng có thể tái sử dụng

Vấn đề

Object pools (hay còn gọi là resource pool – nơi tập hợp tài nguyên, bể chứa các đối tượng) được sử dụng để quản lý bộ nhớ đệm lưu trữ các đối tượng. Client có quyền truy cập tới vào Object Pool thay vì tạo ra đối tượng mới thì đơn giản yêu cầu Object Pool cho một đối tượng đã có sẵn trong nó để thay thế. Pool sẽ liên tục phát triển, ví dụ: pool sẽ tạo các đối tượng mới nếu pool còn trống, hay chúng ta có một pool, hạn chế được số lượng các đối tượng được tạo ra.

Nó được kỳ vọng giữ tất cả Reusable objects (các đối tượng có thể tái sử dụng) mà hiện tại không được sử dụng trong cùng một object pool, do đó chúng có thể được quản lý một cách hợp lý, mạch lac. Để đạt được điều này, lớp Reusable Pool được thiết kế thành lớp singleton(duy nhất).

Thảo luận

Object Pool cho phép lấy các đối tượng từ nó, khi những đối tượng này không còn cần thiết vì đã hoàn tất quá trình, chúng sẽ được trả về pool để được tái sử dụng lại.

Tuy nhiên, ta không muốn chờ đợi quá trình một đối tượng cụ thể được giải phóng, vì vậy Object Pool cũng tạo ra các đối tượng mới khi chúng được yêu cầu, nhưng cũng phải thực hiện sàng lọc, làm sạch các đối tượng không sử dụng theo định kỳ.

Cấu trúc

Ý tưởng chung cho mô hình Connection Pool là nếu các instance(thể hiện) của một lớp có thể được tái sử dụng, thay vị khơi tạo các instance của lớp khi cần, bạn có thể tái sử dụng lại chúng.

  • Reusable – Các khởi tạo của các lớp trong vai trò kết hợp với các đối tượng khác trong một khoảng thời gian nhất định, sau đó chúng không còn cần thiết nữa.
  • Client – Reusable objects.Các khởi tạo của lớp sử dụng các đối tượng Reusable
  • ReusablePool – Các đối tượng khởi tạo của các lớp quản lý các đối tượng Reusable để cung cấp cho các đối tượng Client.

Thông thường, Nó giữ tất cả Reusable objects mà hiện tại không được sử dụng trong cùng một object pool, do đó chúng có thể được quản lý một cách hợp lý, mạch lac. Để đạt được điều này, lớp Reusable Pool được thiết kế thành lớp singleton. Các hàm tạo là dạng private, buộc các lớp khác gọi phương thức getInstance để lấy một khởi tạo từ ReusablePool.

Client gọi một đối tượng của ReusablePool để lấy được phương thức Reusable thì cần có một được tượng Reusable. Đối tượng ReusablePool duy trì tập hợp các đối tượng Reusable. Tập hợp này chứa một nhóm các đối tượng Reusable mà hiện tại chúng không sử dụng.

Khi phương thức acquireReusable được gọi mà trong pool(bể chứa) tồn tại đối tượng Reusable, nó sẽ loại bỏ đối tượng Reusable từ pool và trả lại. Nếu pool trống thì phương thức acquireReusable có thể tạo ra một đối tượng Reusable. Nếu không, nó sẽ đợi cho đến khi đối tượng Reusable được trả về collection(tập hợp các đối tượng Reusable).

Các đối tượng Client truyền một đối tượng Reusable tới ReusablePool thông qua phương thức releaseReusable khi chúng dùng xong đối tượng. Phương thức releaseReusable trả về một đối tượng Reusable tới pool chứa các đối tượng Reusable mà không sử dụng.

Trong nhiều ứng dụng của mô hình Object Pool, tồn tại nhiều lý do cho việc hạn chế tổng số lượng các đối tượng Reusable. Trong những trường hợp như vậy, Reusable Pool tạo ra các đối tượng Reusable chịu trách nhiệm không tạo ra nhiều hơn số lượng các đối tượng Reusable. Nếu đối tượng Reusable Pool chịu trách nhiệm hạn chế số lượng các đối tượng mà chúng tạo ra thì lớp Reusable Pool có phương thức để xác định số lượng tối đá mà các đối tượng được tạo. Phương thức đó được chỉ ra trong sơ đồ phía trên: setMaxPoolSize

Ví dụ

Mô hình Object pool tương tự như một kho văn phòng. Khi thuê nhân viên mới, quản lý văn phòng phải chuẩn bị không gian làm việc cho họ. Trong kho văn phòng liệu có hay không các trang thiết bị. Nếu có, nó sẽ được sử dụng. Nếu không, sẽ phải đặt hàng để mua thiết bị mới từ Amazon. Trong trường hợp nhân viên bị sa thải, vật dụng của anh ta sẽ được chuyển vào nhà kho, ở đó có thể lấy vật dụng cần thiết cho một nơi làm việc mới.

Kiểm tra

  1. Tạo lớp ObjectPool, bên trong nó là một mảng Objects dạng private
  2. Tạo các phương thức lấy ra và giải phóng trong lớp ObjectPool
  3. Chắc chắn ObjectPool là Singleton(thể hiện duy nhât)

Kinh nghiệm thực tiễn

  • Mô hình Factory Method có thể được sử dụng để đóng gói các logic được tạo ra cho các đối tượng. Tuy nhiên, nó tạo ra nhưng không quản lý chúng, mô hình Object Pool theo dõi các đối tượng được tạo ra.
  • Object Pool thường thực hiện như Singleton

Ví dụ áp dụng Object Pool Design Pattern

ObjectPool Class

public abstract class ObjectPool {
    private long expirationTime;

    private Hashtable<T, Long> locked, unlocked;

    public ObjectPool() {
        expirationTime = 30000; // 30 seconds
        locked = new Hashtable<T, Long>();
        unlocked = new Hashtable<T, Long>();
    }

    protected abstract T create();

    public abstract boolean validate(T o);

    public abstract void expire(T o);

    public synchronized T checkOut() {
        long now = System.currentTimeMillis();
        T t;
        if (unlocked.size() > 0) {
            Enumeration e = unlocked.keys();
            while (e.hasMoreElements()) {
                t = e.nextElement();
                if ((now - unlocked.get(t)) > expirationTime) {
                    // đối tượng đã hết hạn
                    unlocked.remove(t);
                    expire(t);
                    t = null;
                } else {
                    if (validate(t)) {
                        unlocked.remove(t);
                        locked.put(t, now);
                        return (t);
                    } else {
                        // kiểm tra đối tượng lỗi
                        unlocked.remove(t);
                        expire(t);
                        t = null;
                    }
                }
            }
        }
        // không có đối tượng nào tồn tại, tạo một đối tượng mới 
        t = create();
        locked.put(t, now);
        return (t);
    }

    public synchronized void checkIn(T t) {
        locked.remove(t);
        unlocked.put(t, System.currentTimeMillis());
    }
}

// Ba phương thức còn lại là trừu tượng
// do đó phải được thực hiện bởi lớp con

public class JDBCConnectionPool extends ObjectPool {

  private String dsn, usr, pwd;

  public JDBCConnectionPool(String driver, String dsn, String usr, String pwd) {
    super();
    try {
      Class.forName(driver).newInstance();
    } catch (Exception e) {
      e.printStackTrace();
    }
    this.dsn = dsn;
    this.usr = usr;
    this.pwd = pwd;
  }

  @Override
  protected Connection create() {
    try {
      return (DriverManager.getConnection(dsn, usr, pwd));
    } catch (SQLException e) {
      e.printStackTrace();
      return (null);
    }
  }

  @Override
  public void expire(Connection o) {
    try {
      ((Connection) o).close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }

  @Override
  public boolean validate(Connection o) {
    try {
      return (!((Connection) o).isClosed());
    } catch (SQLException e) {
      e.printStackTrace();
      return (false);
    }
  }
}

JDBCConnectionPool cho phép ứng dụng mượn và trả các kết nối của cơ sở dữ liệu

public class Main {
  public static void main(String args[]) {
    // Do something...
    ...

    // Tạo ConnectionPool:
    JDBCConnectionPool pool = new JDBCConnectionPool(
      "org.hsqldb.jdbcDriver", "jdbc:hsqldb://localhost/mydb",
      "sa", "secret");

    // Truy cập kết nối:
    Connection con = pool.checkOut();

    // Sử dụng kết nối...

    // Return the connection:
    pool.checkIn(con);
 
  }
}

Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

Leave a Reply

Your email address will not be published. Required fields are marked *