Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Название и классификация паттерна




Одиночка - паттерн, порождающий объекты.

Паттерн Singleton

Назначение

Гарантирует, что у класса есть только один экземпляр, и предоставляет к нему

глобальную точку доступа.

Мотивация

Для некоторых классов важно, чтобы существовал только один экземпляр. Хотя

в системе может быть много принтеров, но возможен лишь один спулер. Должны

быть только одна файловая система и единственный оконный менеджер. В циф-

ровом фильтре может находиться только один аналого-цифровой преобразователь

(АЦП). Бухгалтерская система обслуживает только одну компанию.

Как гарантировать,что у класса есть единственный экземпляр и что этот эк-

земпляр легко доступен? Глобальная переменная дает доступ к объекту, но не за-

прещает инстанцировать класс в нескольких экземплярах.

Более удачное решение - сам класс контролирует то, что у него есть только

один экземпляр, может запретить создание дополнительных экземпляров, пере-

хватывая запросы на создание новых объектов, и он же способен предоставить

доступ к своему экземпляру. Это и есть назначение паттерна одиночка.

Применимость

Используйте паттерн одиночка, когда:

а должен быть ровно один экземпляр некоторого класса, легко доступный

всем клиентам;

а единственный экземпляр должен расширяться путем порождения подклас-

сов, и клиентам нужно иметь возможность работать с расширенным экземп-

ляром без модификации своего кода.

Структура

Участники

a Singleton - одиночка:

- определяет операцию Instance, которая позволяет клиентам получать

доступ к единственному экземпляру. Instance - это операция класса, то

есть метод класса в терминологии Smalltalk и статическая функция-член

в C++;

- может нести ответственность за создание собственного уникального эк-

земпляра.

Порождающие паттерны

Отношения

Клиенты получают доступ к экземпляру класса Singleton только через его

операцию Instance.

Результаты

У паттерна одиночка есть определенные достоинства:

Q контролируемый доступ к единственному экземпляру. Поскольку класс

Singleton инкапсулирует свой единственный экземпляр, он полностью

контролирует то, как и когда клиенты получают доступ к нему;

а уменьшение числа имен. Паттерн одиночка - шаг вперед по сравнению с гло-

бальными переменными. Он позволяет избежать засорения пространства

имен глобальными переменными, в которых хранятся уникальные экземп-

ляры;

а допускает уточнение операций и представления. От класса Singleton мож-

но порождать подклассы, а приложение легко сконфигурировать экземп-

ляром расширенного класса. Можно конкретизировать приложение экземп-

ляром того класса, который необходим во время выполнения;

а допускает переменное число экземпляров. Паттерн позволяет вам легко изме-

нить свое решение и разрешить появление более одного экземпляра класса

Singleton. Вы можете применять один и тот же подход для управления чис-

лом экземпляров, используемых в приложении. Изменить нужно будет лишь

операцию, дающую доступ к экземплярукласса Singleton;

а большая гибкость, чем у операций класса. Еще один способ реализовать функ-

циональность одиночки - использовать операции класса, то есть статичес-

кие функции-члены в C++ и методы класса в Smalltalk. Но оба этих приема

препятствуют изменению дизайна, если потребуется разрешить наличие

нескольких экземпляров класса. Кроме того, статические функции-члены

в C++ не могут быть виртуальными, так что их нельзя полиморфно замес-

тить в подклассах.

Реализация

При использовании паттерна одиночка надо рассмотреть следующие вопросы:

а гарантирование единственного экземпляра. Паттерн одиночка устроен так,

что тот единственный экземпляр, который имеется у класса, - самый обыч-

ный, но больше одного экземпляра создать не удастся. Чаще всего для этого

прячут операцию, создающую экземпляры, за операцией класса (то есть за

статической функцией-членом или методом класса), которая гарантирует

создание не более одного экземпляра. Данная операция имеет доступ к пе-

ременной, где хранится уникальный экземпляр, и гарантирует инициализа-

цию переменной этим экземпляром перед возвратом ее клиенту. При таком

подходе можно не сомневаться,что одиночка будет создан и инициализи-

рован перед первым использованием.

В C++ операция класса определяется с помощью статической функции-чле-

на Instance класса Singleton. В этом классе есть также статическая

Паттерн Singleton Jill

переменная-член „instance, которая содержит указатель на уникальный

экземпляр.

Класс Singleton объявлен следующим образом:

class Singleton {

public:

static Singleton* Instance();

protected:

Singleton();

private:

static Singleton* „instance;

};

А реализация такова:

Singleton* Singleton::_instance = 0;

Singleton* Singleton::Instance () {

if (_instance == 0) {

_instance = new Singleton;

}

return „instance;

}

Клиенты осуществляют доступ к одиночке исключительно через функцию-

член Instance. Переменная „instance инициализируется нулем, а ста-

тическая функция-член Instance возвращает ее значение, инициализируя

ее уникальным экземпляром, если в текущий момент оно равно 0. Функция

Instance использует отложенную инициализацию: возвращаемое ей зна-

чение не создается и не хранится вплоть до момента первого обращения.

Обратите внимание, что конструктор защищенный. Клиент, который попы-

тается инстанцировать класс Singleton непосредственно, получит ошиб-

ку на этапе компиляции. Это дает гарантию, что будет создан только один

экземпляр.

Далее, поскольку „instance - указатель на объект класса Singleton, то

функция-член Instance может присвоить этой переменной указатель на

любой подкласс данного класса. Применение возможности мы увидим

в разделе ≪Пример кода≫.

О реализации в C++ скажем особо. Недостаточно определить рассматрива-

емый патерн как глобальный или статический объект, а затем полагаться на

автоматическую инициализацию. Тому есть три причины:

- мы не можем гарантировать, что будет объявлен только один экземпляр

статического объекта;

- у нас может не быть достаточно информации для инстанцирования лю-

бого одиночки во время статической инициализации. Одиночке могут

быть необходимы данные, вычисляемые позже, во время выполнения

программы;

- в C++ не определяется порядок вызова конструкторов для глобальных

объектов через границы единиц трансляции [ES90]. Это означает, что

Порождающие паттерны

между одиночками не может существовать никаких зависимостей. Если

они есть, то ошибок не избежать.

Еще один (хотя и не слишком серьезный) недостаток глобальных/статических

объектов в том, что приходится создавать всех одиночек, даже, если они не

используются. Применение статической функции-члена решает эту проблему.

В Smalltalk функция, возвращающая уникальный экземпляр, реализуется

как метод класса Singleton. Чтобы гарантировать единственность экземп-

ляра, следует заместить операциюnew. Получающийся класс мог бы иметь

два метода класса (в них Solelnstance - это переменная класса, которая

больше нигде не используется):

new

self error: 'не удается создать новый объект1

default

Solelnstance isNil ifTrue: [Solelnstance:= super new].

^ Solelnstance

а порождение подклассов Singleton. Основной вопрос не столько в том, как опре-

делить подкласс, а в том, как сделать, чтобы клиенты могли использовать

его единственный экземпляр. По существу, переменная, ссылающаяся на

экземпляр одиночки, должна инициализироваться вместе с экземпляром

подкласса. Простейший способ добиться этого - определить одиночку, ко-

торого нужно применять в операции Instance класса Singleton. В раз-

деле ≪Пример кода≫ показывается, как можно реализовать эту технику с по-

мощью переменных среды.

Другой способ выбора подкласса Singleton - вынести реализацию опера-

ции Instance из родительского класса (например, MazeFactory) и помес-

тить ее в подкласс. Это позволит программисту на C++ задать класс оди-

ночки на этапе компоновки (скомпоновав программу с объектным файлом,

содержащим другую реализацию), но от клиента одиночка будет по-прежне-

му скрыт.

Такой подход фиксирует выбор класса одиночки на этапе компоновки, за-

трудняя тем самым его подмену во время выполнения. Применение услов-

ных операторов для выбора подкласса увеличивает гибкость решения, но

все равно множество возможных классов Singleton остается жестко ≪за-

шитым≫ в код. В общем случае ни тот, ни другой подход не обеспечивают

достаточной гибкости.

Ее можно добиться за счет использования реестра одиночек. Вместо того

чтобы задавать множество возможных классов Singleton в операции

Instance, одиночки могут регистрировать себя по имени в некотором всем

известном реестре.

Реестр сопоставляет одиночкам строковые имена. Когда операции Instance

нужен некоторый одиночка, она запрашивает его у реестра по имени. Начи-

нается поиск указанного одиночки, и, если он существует, реестр возвраща-

ет его. Такой подход освобождает Instance от необходимости ≪знать≫ все

Паттерн Singleton

возможные классы или экземпляры Singleton. Нужен лишь единый для

всех классов Singleton интерфейс, включающий операции с реестром:

class Singleton {

public:

static void Register(const char* name, Singleton*);

static Singleton* Instance ().;

protected:

static Singleton* Lookup(const char* name);

private:

static Singleton* „instance;

static List<NameSingletonPair>* „registry;

Операция Register регистрирует экземпляр класса Singleton под ука-

занным именем. Чтобы не усложнять реестр, мы будем хранить в нем спи-

сок объектов NameSingletonPair. Каждый такой объект отображает имя

на одиночку. Операция Lookup ищет одиночку по имени. Предположим, что

имя нужного одиночки передается в переменной среды:

Singleton* Singleton::Instance () {

if („instance == 0) {

const char* singletonName = getenv("SINGLETON");

// пользователь или среда предоставляют это имя на стадии

// запуска программы

_instance = Lookup(singletonName);

// Lookup возвращает 0, если такой одиночка не найден

return „instance;

В какой момент классы Singleton регистрируют себя? Одна из возмож-

ностей - конструктор. Например, подкласс MySingleton мог бы работать

так:

MySingleton::MySingleton() {

Singleton::Register("MySingleton", this);

}

Разумеется, конструктор не будет вызван, пока кто-то не инстанцирует

класс, но ведь это та самая проблема, которую паттерн одиночка и пытается

разрешить! В C++ ее можно попытаться обойти, определив статический эк-

земпляр класса My Single ton. Например, можно вставить строку

static MySingleton theSingleton;

в файл, где находится реализация MySingleton.

Теперь класс Singleton не отвечает за создание одиночки. Его основной

обязанностью становится обеспечение доступа к объекту-одиночке из

Порождающие паттерны

любой части системы. Подход, сводящийся к применению статического

объекта, по-прежнему имеет потенциальный недостаток: необходимо созда-

вать экземпляры всех возможных подклассов Singleton, иначе они не бу-

дут зарегистрированы.

Пример кода

Предположим, нам надо определить класс MazeFactory для создания лаби-

ринтов, описанный на стр. 99. MazeFactory определяет интерфейс для построения

различных частей лабиринта. В подклассах эти операции могут переопределять-

ся, чтобы возвращать экземпляры специализированных классов продуктов, на-

пример объекты BombedWall, а не просто Wall.

Существенно здесь то, что приложению Maze нужен лишь один экземпляр

фабрики лабиринтов и он должен быть доступен в коде, строящем любую часть

лабиринта. Тут-то паттерн одиночка и приходит на помощь. Сделав фабрику

MazeFactory одиночкой, мы сможем обеспечить глобальную доступность объек-

та, представляющего лабиринт, не прибегая к глобальным переменным.

Для простоты предположим, что мы никогда не порождаем подклассов от

MazeFactory. (Чуть ниже будет рассмотрен альтернативный подход.) В C++ для

того, чтобы превратить фабрику в одиночку, мы добавляем в класс MazeFactory

статическую операцию Instance и статический член _instance, в котором бу-

дет храниться единственный экземпляр. Нужно также сделать конструктор защи-

щенным, чтобы предотвратить случайное инстанцирование, в результате которо-

го будет создан лишний экземпляр:

class MazeFactory {

public:

static MazeFactory* Instance();

// здесь находится существующий интерфейс

protected:

MazeFactory();

private:

static MazeFactory* „instance;

};

Реализация класса такова:

MazeFactory* MazeFactory::_instance = 0;

MazeFactory* MazeFactory::Instance 0 {

if (_instance == 0) {

_instance = new MazeFactory;

}

return _instance;

}

Теперь посмотрим, что случится, когда у MazeFac tory есть подклассы и опре-

деляется, какой из них использовать. Вид лабиринта мы будем выбирать с по-

мощью переменной среды, поэтому добавим код, которыйинстанцирует нужный

Паттерн Singleton

подкласс MazeFactory в зависимости от значения данной переменной. Лучше

всего поместить код в операцию Instance, поскольку она уже и так инстанциру-

ет MazeFactory:

MazeFactory* MazeFactory::Instance () {

if (_instance == 0) {

const char* mazeStyle = getenv("MAZESTYLE");

if (strcmp(mazeStyle, "bombed") == 0) {

„.instance = new BombedMazeFactory;

} else if (strcmp(mazeStyle, "enchanted") == 0) {

_instance = new EnchantedMazeFactory;

//... другие возможные подклассы

} else { // по умолчанию

_instance = new MazeFactory;

}

}

return _instance;

}

Отметим, что операцию Instance нужно модифицировать при определении

каждого нового подкласса MazeFactory. В данном приложении это, может быть,

и не проблема, но для абстрактных фабрик, определенных в каркасе, такой под-

ход трудно назвать приемлемым.

Одно из решений - воспользоваться принципом реестра, описанным в разде-

ле ≪Реализация≫. Может помочь и динамическое связывание, тогда приложению

не нужно будет загружать все неиспользуемые подклассы.

Известные применения

Примером паттерна одиночка в Smalltalk-80 [РагЭО] является множество из-

менений кода, представленное классом ChangeSet. Более тонкий пример - это

отношение между классами и их метаклассами. Метаклассом называется класс

класса, каждый метакласс существует в единственном экземпляре. У метакласса

нет имени (разве что косвенное, определяемое экземпляром), но он контролирует

свой уникальный экземпляр, и создать второй обычно не разрешается.

В библиотеке Interviews для создания пользовательских интерфейсов

[LCI+92] - паттерн одиночка применяется для доступа к единственным экземпля-

рам классов Session (сессия) и WidgetKit (набор виджетов). Классом Session

определяется главный цикл распределения событий в приложении. Он хранит

пользовательские настройки стиля и управляет подключением к одному или не-

скольким физическим дисплеям. WidgetKit - это абстрактная фабрика для

определения внешнего облика интерфейсных виджетов. Операция Widget-

Kit:: instance () определяет конкретный инстанцируемый подкласс WidgetKit

на основе переменной среды, которую устанавливает Session. Аналогичная опе-

рация в классе Session ≪выясняет≫, поддерживаются ли монохромные или цвет-

ные дисплеи, и соответственно конфигурирует одиночку Session.

Порождающие паттерны

Родственные паттерны

С помощью паттерна одиночка могут быть реализованы многие паттерны. См.

описание абстрактной фабрики, строителя и прототипа.





Поделиться с друзьями:


Дата добавления: 2015-11-23; Мы поможем в написании ваших работ!; просмотров: 467 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Студент может не знать в двух случаях: не знал, или забыл. © Неизвестно
==> читать все изречения...

2817 - | 2385 -


© 2015-2025 lektsii.org - Контакты - Последнее добавление

Ген: 0.012 с.