Породжуючі патерни:
- Абстрактна фабрика (Abstract factory)
- Будівельник (Builder)
- Фабричний метод (Factory method)
- Прототип (Prototype)
- Одинак (Singleton)
Дійшовши до наступного елементу списку породжуючих патернів, розглянемо дизайн-патерн «Фабричний метод».
Фабричний метод (Factory method)
Призначення: інстанціювати клас, не знаючи його реалізації.
Фактично, даний патерн дозволяє створювати об'єкти якогось класу і при цьому не заглиблюватися в його оголошення та визначення.
Як приклад можна розглядати базу даних бібліотеки. В базі даних містяться книги різних жанрів. Якщо ви хочете прочитати «Муму», то щоб не ритися в манускриптах та в сувоях з заклинаннями, а одразу отримати потрібний жанр і вже вибирати книгу цього жанру, можна використати даний патерн «Фабричний метод».
Отже, створимо клас Book
, методи якого можуть повертати список книг, їхні назви або проводити якісь операції, наприклад, прочитати книгу або купити.
enum eBookGenre
{
FICTION,
FANTASY,
NOVEL
};
class Book
{
public:
Book();
~Book();
virtual void readBook() = 0;
virtual void buyBook() = 0;
virtual void bookName() = 0;
};
Клас Book
є абстрактним, оскільки є загальним визначенням, яке необхідно реалізувати в класах-нащадках.
Як можна побачити, перед класом реалізоване перерахування eBookGenre
, в якому перелічуються жанри книг: художня література, фентезі та роман.
Оскільки жанрів три, необхідно оголосити похідні від Book
класи, кожен з яких представляє відповідний літературний жанр та реалізувати в них чисті віртуальні функції readBook()
, buyBook()
та bookName()
.
Фентезі:
#include "Book.h"
class FantasyBook : public Book
{
public:
FantasyBook();
~FantasyBook();
virtual void readBook();
virtual void buyBook();
virtual void bookName();
};
Художня література:
#include "Book.h"
class FictionBook : public Book
{
public:
FictionBook();
~FictionBook();
virtual void readBook();
virtual void buyBook();
virtual void bookName();
};
Роман:
class NovelBook : public Book
{
public:
NovelBook();
~NovelBook();
virtual void readBook();
virtual void buyBook();
virtual void bookName();
};
Реалізація віртуальних функцій на прикладі класу Fantasy
:
#include "FantasyBook.h"
FantasyBook::FantasyBook()
{
}
FantasyBook::~FantasyBook()
{
}
void FantasyBook::readBook()
{
cout << "You are reading the fantasy book." << endl;
}
void FantasyBook::buyBook()
{
cout << "You bought the fantasy book. And now you are ork Aphanasiy." << endl;
}
void FantasyBook::bookName()
{
cout << "The fantasy book \\"Dragon's Fire\\" by Emperor Nero." << endl;
}
Нехай кожна функція виводить якесь повідомлення. В реальних завданнях можна було б повертати список книг. В даному випадку важливий тільки принцип роботи, що забезпечується патерном.
Тепер необхідно створити клас, що містить фабричний метод, котрий повертатиме жанр книги відповідно до переданого параметра.
#include "FantasyBook.h"
#include "NovelBook.h"
#include "FictionBook.h"
class BookFactory
{
public:
BookFactory();
~BookFactory();
Book* getBook(eBookGenre& b);
};
Оголошена одна-єдина функція getBook(eBookGenre& b)
, яка приймає в якості параметра елемент з перерахування eBookGenre
. Ця функція і є фабричним методом.
Визначення фабричного методу наступне:
Book* BookFactory::getBook(eBookGenre& b)
{
switch (b)
{
case FICTION:
return new FictionBook();
break;
case NOVEL:
return new NovelBook();
break;
case FANTASY:
return new FantasyBook();
break;
default:
cout << "No such book" << endl;
break;
}
}
У відповідності з переданим параметром повертаються об'єкти потрібних класів. Наприклад, якщо b == FICTION
– повернути об'єкт типу FictionBook
.
Використання:
#include "BookFactory.h"
int main()
{
eBookGenre book = NOVEL; // визначається жанр - роман
BookFactory bf; // інстанціювання класу-фабрики
Book* bk = bf.getBook(book); // повернути об'єкт жанру роман за допомогою фабричного методу
bk->bookName();
bk->buyBook();
bk->readBook();
delete bk;
return 0;
}
Власне від класу BookFactory
можна створити похідні класи та зробити фабричний метод getBook(eBookGenre& b)
віртуальним. Це дозволить розширити можливості створення об'єктів. Один нащадок (нехай BookFactoryDelivered1) повертатиме жанр книги у відповідності від переданого параметра (як у вищезгаданому прикладі), а другий (BookFactoryDelivered2) при такому самому параметрі поверне інший об'єкт:
Book* BookFactoryDelivered2::getBook(eBookGenre& b)
{
switch (b)
{
case FICTION:
return new FictionBook();
break;
case NOVEL:
// якщо параметр b == NOVEL, то повернеться
// об'єкт класу NovelAndFantasyBook, що містить
// книги, які належать одночасно до фентезі та романів
// в той час як перший (BookFactoryDelivered1) поверне
// тільки об'єкт класу, що керує жанром NovelBook - тільки романи
return new NovelAndFantasyBook();
break;
case FANTASY:
return new FantasyBook();
break;
default:
cout << "No such book" << endl;
break;
}
}
Підсумки
Дизайн-патерн «Фабричний метод» не є складним і забезпечує абстрагування, яке дозволяє програмісту отримати необхідний об'єкт, не знаючи, який саме клас він реалізує.
Алгоритм використання патерна:
- Створити описи груп, по яких можна групувати об'єкти (
enum eBookGenre
, але необов'язково використовувати самеenum
); - Створити абстрактний клас, об'єкти якого можуть ділитися на ці групи (
Book
); - Реалізувати похідні класи, що визначають групи (
FictionBook
,NovelBook
, ...); - Створити клас, який містить фабричний метод, що повертає необхідний об'єкт (
BookFactory
).
Завантажити вихідний код до статті за посиланням.
Ще немає коментарів