Паттерн (шаблон) проектування Object Pool (пул об’єктів)
Призначення патерну Object Pool
Застосування патерна Object Pool може значно збільшити продуктивність системи; його використання найефективніше у ситуаціях, коли створення екземплярів деякого класу вимагає великих витрат, об'єкти у системі створюються часто, але кількість створюваних об'єктів за одиницю часу обмежена.
Вирішувана проблема
Пули об'єктів (відомі також як пули ресурсів) використовуються для керування кешуванням об'єктів. Клієнт, який має доступ до пулу об'єктів, може уникнути створення нових об'єктів, просто запитуючи в пулі вже створений екземпляр. Пул об'єктів може бути зростаючим, коли за відсутності вільних створюються нові об'єкти або з обмеженням кількості об'єктів, що створюються.
Бажано, щоб усі багаторазово використовувані об'єкти, вільні у певний час, зберігалися у тому самому пулі об'єктів. Тоді ними можна управляти на основі єдиної політики. Для цього клас Object Pool проектується за допомогою патерну Singleton.
Обговорення патерну Object Pool
Процеси вимагають об'єкти з пулу об'єктів. Коли ці об'єкти більше не потрібні, вони повертаються до пулу для подальшого повторного використання.
Якщо при черговому запиті всі об'єкти пулу зайняті, процес очікуватиме звільнення об'єкта. Для виключення подібної ситуації пул об'єктів повинен уміти створювати нові об'єкти за необхідності. При цьому він також повинен реалізовувати механізм періодичного очищення об'єктів, що не використовуються.
Структура патерну Object Pool
Основна ідея патерну Object Pool полягає в тому, щоб уникнути створення нових екземплярів класу у разі можливості їх повторного використання.
UML-діаграма класів патернуObject Pool
Як правило, бажано зберігати всі об'єкти Reusable в тому самому пулі об'єктів. Це дозволяє управляти ними з урахуванням єдиної політики. Для цього клас ReusablePool проектується як Singleton. Його конструктори оголошуються як private, тому єдиний екземпляр класу ReusablePool доступний іншим класам лише через метод getInstance() .
Клієнт запитує об'єкт Reusable через метод acquireReusable() об'єкта класу ReusablePool . Об'єкт ReusablePool містить колекцію повторно використовуваних об'єктів Reusable для побудови пулу.
Якщо при викликі методу acquireReusable() у пулі є вільні об'єкти, acquireReusable() видаляє об'єкт Reusable з пулу і повертає його. Якщо ж пул порожній, метод acquireReusable() створює новий об'єкт Reusable , якщо це передбачено реалізацією. Якщо ж метод acquireReusable() не може створювати нові об'єкти, він чекає, поки повторно використовуваний об'єкт не повернеться до колекції.
Після використання клієнт передає об'єкт Reusable метод releaseReusable() об'єкта ReusablePool . Метод releaseReusable() повертає цей об'єкт у пул вільних для повторного використання об'єктів.
У багатьох додатках із застосуванням патерна Object Pool існують причини обмеження загальної кількості існуючих об'єктів Reusable . У таких випадках об'єкт ReusablePool , що створює об'єкти Reusable , відповідає за створення числа об'єктів Reusable , що не перевищує вказаного максимального. Якщо об'єкт ReusablePool несе відповідальність за обмеження кількості об'єктів, які він створює, то клас ReusablePool повинен мати метод для визначення максимальної кількості створюваних об'єктів. На малюнку вище, цей метод позначений як setMaxPoolSize() .
Приклад патернуObject Pool
Вам подобається боулінг? Якщо так, то ви, напевно, знаєте, що маєте змінити ваше взуття при відвідуванні боулінг-клубу. Полиця із взуттям – чудовий приклад пулу об'єктів. Коли ви хочете грати, ви берете з неї пару (acquireReusable). А після гри, ви повертаєте взуття назад на полицю (releaseReusable).