Паттерн Singleton
Часть раздела Объектно-ориентированное программирование путешествия по C на Coddy. Урок 54 из 61.
Иногда вам нужен ровно один экземпляр объекта во всей программе: менеджер конфигурации, система ведения журналов или подключение к базе данных. Паттерн Singleton гарантирует, что существует только один экземпляр, и предоставляет глобальную точку доступа к нему.
В C мы реализуем это с помощью переменной static внутри исходного файла. Помни, что static в области видимости файла означает, что переменная является приватной для этого файла и сохраняется в течение всего времени работы программы. В сочетании с функцией getter это создаёт наш Singleton:
// config.c
#include "config.h"
#include <stdlib.h>
struct Config {
int max_connections;
int timeout;
};
static Config* instance = NULL;
Config* get_config(void) {
if (instance == NULL) {
instance = malloc(sizeof(Config));
instance->max_connections = 100;
instance->timeout = 30;
}
return instance;
}Магия происходит в get_config(). Первый вызов создаёт и инициализирует экземпляр. Каждый последующий вызов возвращает тот же указатель.
Ключевое слово static гарантирует, что instance сохраняет своё значение между вызовами функции и остаётся скрытым от других файлов.
Из заголовочного файла пользователи видят только getter function. Они не могут напрямую получить доступ к static variable или изменить её. Независимо от того, сколько раз и откуда вы call get_config(), вы всегда получаете тот же объект.
Задание
ЛегкоДавайте создадим модуль Settings, который управляет конфигурацией всего приложения с использованием шаблона Singleton. Независимо от того, сколько раз ваш код запрашивает настройки, он всегда должен получать в точности тот же экземпляр.
Вы организуете код в трёх файлах:
settings.h: Объявите непрозрачный типSettingsс помощью предварительного объявления (тело структуры остаётся скрытым). Объявитеget_settingsfunction, которая возвращает pointer на singleton instance, а также getter и setter functions для двух значений конфигурации:volume(целое число) иbrightness(целое число). Защитные макросы включения здесь обязательны.settings.c: Здесь происходит самое важное. Определите фактическую структуруSettingsс полямиvolumeиbrightness. Создайте pointer variable с классом храненияstaticдля единственного instance (инициализированный значениемNULL). Реализуйтеget_settingsтак, чтобы first call выделял память для instance и инициализировал его значениями по умолчанию (volume: 50, brightness: 75), а subsequent calls просто возвращали existing instance. Реализуйте getter и setter functions для доступа к скрытым полям и их изменения.main.c: Продемонстрируйте, что Singleton работает правильно. Считайте два целых числа, представляющих новые значения volume и brightness. Callget_settingsдважды, сохранив результаты в двух different pointer variables. Используйте first pointer для обновления volume и brightness с помощью input values. Затем используйте second pointer, чтобы retrieve и print оба значения. Если Singleton работает, изменения, внесённые через один pointer, будут видны через другой.
Ваша программа получит два входных значения:
- Новое значение volume
- Новое значение brightness
Выведите volume и brightness в отдельных строках после их изменения через один pointer и чтения через другой.
Пример вывода, если входные значения — 80 и 100:
80
100Пример вывода, если входные значения — 25 и 60:
25
60Пример вывода, если входные значения — 0 и 50:
0
50Главная идея заключается в том, что both pointers указывают на один и тот же объект в памяти: в этом и состоит суть шаблона Singleton. static variable внутри вашего исходного файла гарантирует, что будет существовать only one instance, и этот instance сохраняется в течение всего времени работы программы.
Попробуйте сами
#include <stdio.h>
#include "settings.h"
int main() {
int new_volume, new_brightness;
scanf("%d", &new_volume);
scanf("%d", &new_brightness);
// TODO: Вызовите get_settings дважды и сохраните в две разные переменные-указатели
// TODO: Используйте первый указатель для обновления volume и brightness входными значениями
// TODO: Используйте второй указатель для получения и вывода volume и brightness
// (Это демонстрирует, что оба указателя указывают на один и тот же экземпляр singleton)
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигурПотренируйтесь самостоятельно: Онлайн-компилятор C