Паттерн Factory
Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 55 из 61.
Паттерн «Фабрика» централизует создание объектов в одной функции. Вместо того чтобы разбрасывать вызовы malloc и код инициализации по всей программе, вы вызываете одну функцию, которая решает, что именно создать, и возвращает готовый к использованию объект.
Этот паттерн особенно эффективен в сочетании с полиморфизмом. Фабрика может возвращать различные конкретные типы через общий базовый указатель, скрывая детали создания от вызывающей стороны. Вызывающая сторона просто запрашивает «фигуру», а фабрика решает, создать ли круг, прямоугольник или что-то другое.
Вот типичная реализация фабрики:
typedef enum {
SHAPE_CIRCLE,
SHAPE_RECTANGLE
} ShapeType;
Shape* create_shape(ShapeType type) {
switch (type) {
case SHAPE_CIRCLE:
return (Shape*)create_circle(5.0);
case SHAPE_RECTANGLE:
return (Shape*)create_rectangle(4.0, 3.0);
default:
return NULL;
}
}Фабрика принимает идентификатор (обычно перечисление enum) и использует оператор switch, чтобы определить, какой конструктор вызвать. Она возвращает Shape* независимо от фактически созданного типа. Вызывающей стороне не нужно знать о Circle или Rectangle — она просто работает с базовым типом:
Shape* s = create_shape(SHAPE_CIRCLE);
s->draw(s); // полиморфный вызов
free_shape(s);Этот паттерн упрощает добавление новых типов: достаточно добавить новое значение enum и новый case в фабрику. Остальная часть вашего кода останется неизменной.
Задание
ЛегкоДавайте создадим Pet Factory (Фабрику питомцев) — систему, которая создает различные типы питомцев через одну функцию-фабрику. Вместо того чтобы распределять логику создания по всему коду, вы централизуете ее в одном месте, которое решает, какой тип питомца создать на основе входного идентификатора.
Вы организуете свой код в трех файлах:
pet.h: Определите перечислениеPetTypeсо значениямиPET_DOG,PET_CATиPET_BIRD. Создайте базовую структуруPet, которая содержит указатель на функциюspeak(которая принимаетconst Pet*и ничего не возвращает). Объявите функцию-фабрикуcreate_pet, которая принимаетPetTypeи возвращаетPet*, а также функциюfree_petдля очистки памяти.pet.c: Определите три конкретные структуры питомцев —Dog,CatиBird— каждая из которых содержитPetв качестве первого члена. Реализуйте функцию speak для каждого типа:- Собаки выводят:
Woof! - Кошки выводят:
Meow! - Птицы выводят:
Tweet!
create_pet— используйте оператор switch поPetType, чтобы вызвать нужный конструктор и вернуть результат в видеPet*. Для недопустимых типов возвращайтеNULL. Реализуйтеfree_petдля освобождения выделенной памяти.- Собаки выводят:
main.c: Считайте целое число, представляющее тип питомца (0 для собаки, 1 для кошки, 2 для птицы). Используйте свою фабрику для создания соответствующего питомца, вызовите его функциюspeakполиморфно через указатель на базовый типPet*, а затем освободите память питомца.
Ваша программа получит один входной параметр: целое число, указывающее, какого питомца создать.
Пример вывода, когда на входе 0:
Woof!Пример вывода, когда на входе 1:
Meow!Пример вывода, когда на входе 2:
Tweet!Прелесть паттерна «Фабрика» заключается в том, что main.c не нужно знать о структурах Dog, Cat или Bird — он работает только с базовым типом Pet. Добавление нового типа питомца в будущем означает добавление нового значения в перечисление и нового варианта в фабрике без изменения кода, который использует питомцев.
Шпаргалка
Паттерн «Фабрика» (Factory pattern) централизует создание объектов в одной функции, скрывая детали создания от вызывающей стороны.
Фабричная функция принимает идентификатор (обычно перечисление enum) и использует оператор switch для определения того, какой конструктор вызвать, возвращая указатель на базовый тип:
typedef enum {
SHAPE_CIRCLE,
SHAPE_RECTANGLE
} ShapeType;
Shape* create_shape(ShapeType type) {
switch (type) {
case SHAPE_CIRCLE:
return (Shape*)create_circle(5.0);
case SHAPE_RECTANGLE:
return (Shape*)create_rectangle(4.0, 3.0);
default:
return NULL;
}
}Вызывающая сторона работает только с базовым типом, что обеспечивает полиморфное поведение:
Shape* s = create_shape(SHAPE_CIRCLE);
s->draw(s); // polymorphic call
free_shape(s);Преимущества: Добавление новых типов требует лишь добавления нового значения enum и варианта switch, без изменения существующего кода, использующего фабрику.
Попробуйте сами
#include <stdio.h>
#include "pet.h"
int main(void) {
int type;
scanf("%d", &type);
// TODO: Используйте фабрику для создания соответствующего питомца
// Hint: Приведите int к PetType
// TODO: Вызовите функцию speak полиморфно через указатель Pet*
// TODO: Освободите питомца
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга