Menu
Coddy logo textTech

Паттерн 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(), вы всегда получаете тот же объект.

challenge icon

Задание

Легко

Давайте создадим модуль Settings, который управляет конфигурацией всего приложения с использованием шаблона Singleton. Независимо от того, сколько раз ваш код запрашивает настройки, он всегда должен получать в точности тот же экземпляр.

Вы организуете код в трёх файлах:

  • settings.h: Объявите непрозрачный тип Settings с помощью предварительного объявления (тело структуры остаётся скрытым). Объявите get_settings function, которая возвращает 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. Call get_settings дважды, сохранив результаты в двух different pointer variables. Используйте first pointer для обновления volume и brightness с помощью input values. Затем используйте second pointer, чтобы retrieve и print оба значения. Если Singleton работает, изменения, внесённые через один pointer, будут видны через другой.

Ваша программа получит два входных значения:

  1. Новое значение volume
  2. Новое значение 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;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C