Дизайн-патерни: Фабричний метод (С++)

2 хв. читання

Породжуючі патерни:

Дійшовши до наступного елементу списку породжуючих патернів, розглянемо дизайн-патерн «Фабричний метод».

Фабричний метод (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;
	}
}

Підсумки

Дизайн-патерн «Фабричний метод» не є складним і забезпечує абстрагування, яке дозволяє програмісту отримати необхідний об'єкт, не знаючи, який саме клас він реалізує.

Алгоритм використання патерна:

  1. Створити описи груп, по яких можна групувати об'єкти (enum eBookGenre, але необов'язково використовувати саме enum);
  2. Створити абстрактний клас, об'єкти якого можуть ділитися на ці групи (Book);
  3. Реалізувати похідні класи, що визначають групи (FictionBook, NovelBook, ...);
  4. Створити клас, який містить фабричний метод, що повертає необхідний об'єкт (BookFactory).

Завантажити вихідний код до статті за посиланням.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 6.6K
Приєднався: 6 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація