Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_5449.html
 

О глобальных переменных и RTTI#9

Опубликовано 18.05.2012 г. 18:37

Бытует мнение, что глобальные переменные - большое зло. Как можно поступить, чтобы инкапcулировать множество глобальных переменных простых типов в класс, и реализовать механизм уведомлений об их изменениях?

Именно о таком я сегодня задумался. В дейстивтельности тема глобальных переменных весьма избита, и врядли здесь можно сказать что-то новое. Вывод всегда может быть один: от них нужно избавляться. Собственно главный минус, конечно же, в том, что глобальную переменную любой может изменить, при этом никакие другие модули не будут знать о таком изменении. В проекте, над которым я работаю, глобальных переменных достаточно много. Раньше было еще больше, но много чего удалось почистить. И вот я задумался, как можно легко поместить глобальные переменные внутрь некоторого класса (очевидно в виде свойств) с написанием небольшого кода так, чтобы он реализовывал механизм уведомлений при изменении. В общем то не проблема взять и сгруппировать все переменные в одном классе. Но в таком случае при описании свойства (на которое мы заменяем глобальную переменную) нам придется написать также и внутренее поле, которое хранит ее значение, нужны методы для чтения и записи значения. Метод записи при этом должен как то рассылать уведомления.

Идея, которая меня посетила, аналогична в этой части методу, который был применен в статье про настройки приложения: Настройки программы в INI с использованием RTTI#7. Суть была следующая: для описания новой настройки,  нам необходимо добавить только одну единственную строку в класс описания группы настроек. Так и  здесь, для расширения числа глобальных переменных необходимо добавить толко одну строку в класс описания. В итоге получаем следующее. Допустим у нас в проекте было три глобальных переменных: CurrentYear, Angle, Title. Теперь мы заменим все упоминания этих переменных в коде на эквиваленты вда globals.CurrentYear и т.д. Переменная globals - это экземпляр класса TGlobals, который описывается следующим образом:

    TGlobals = class(TGlobalsContainer)
      public
        property CurrentYear : integer  index 0 read getIntValue write setIntValue;
        property Angle : double         index 1 read getFloatValue write setFloatValue;
        property Title : string         index 2 read getStringValue write setStringValue;
    end;

Для включения и использования в программе новой глобальной переменной необходимо лишь дописать новое свойство в класс, это избавляет нас от написания внутренних членов класса и методов их изменения. Как и в случае вышеупомянутой статьи про INI-файл с настройками, используется нумерация свойств с помощью index. Функционал  скрыт в базовом класса TGlobalsContainer:

    TGlobalsContainer = class(TObject)
      strict private
        type
          TGlobalValue = class(TObject)
            propName : string;
            eventList : TList<TNotifyEvent>;

            FloatValue : double;
            IntValue : integer;
            StringValue : string;
            constructor Create();
            destructor Destroy(); override;
          end;

        var
          FValues : TObjectList<TGlobalValue>;
          FUpdating : boolean;

        procedure DoNotify(idx : integer);
        procedure InitGlobalValues();
      strict protected
        function  getIntValue(idx: integer):integer;
        procedure setIntValue(idx : integer; aValue : integer);
        function  getFloatValue(idx: integer):double;
        procedure setFloatValue(idx: integer; aValue:double);
        function  getStringValue(idx:integer):string;
        procedure setStringValue(idx:integer; aValue: string);
      public
        constructor Create();
        destructor  Destroy(); override;

        procedure BeginUpdate();
        procedure EndUpdate();

        procedure AddEventHandler(propertyName : string; eventHandler : TNotifyEvent);
        procedure RemoveEventHandler(propertyName : string; eventHandler : TNotifyEvent = nil);
    end;

Доступ к полям TGlobals основан на их индексах и соответствующих методах getIntValue и т.п. Внутри себя TGlobalsContainer содержит список объектов TGlobalValue, которые отвечают за хранение значений бывших глобальных переменных, а также обработчиков событий их изменний. Итак, обращение к какому-либо свойству TGlobals ведет к вызову метода getIntValue/getFloatValue/getStringValue при чтении, и соответствующих set-методов при записи. Каждый из методов возвращает соответствующее поле IntValue/FloatValue/StringValue элемента списка FValuesList с индексом idx (этим индексом пронумеровано свойство в TGlobals). При вызове set-метода, циклически вызываются зарегистрированнные обработчики изменения значений из eventList элемента.

Поскольку весь функционал скрыт в TGlobalsContainer, то расширять TGlobals новыми свойствами весьма легко и быстро. Так же мы можем внедрить дополнтельный функционал для управления свойствами путем использования атрибутов.

Такая реализация, конечно же, в разы медленней чем использование простых переменных. Но если они редко используются, то такой подход имеет право на жизнь. Так же стоит отметить, что такую перменную не удастся преедать по ссылке. Отмечу, что здесь приведен только подход - пища для размышений, реализация не претендует на хорошую, а является лишь наброском. Исходный код с реализацией прикреплен ниже.

Метки:  generics  |  rtti 

Комментарии

Vlad
19.05.2012 в 10:47
>>Как можно поступить, чтобы инкапcулировать множество глобальных переменных простых типов в класс, и реализовать механизм уведомлений об их изменениях?

Использовать LiveBindings =) В сеттерах свойств класса вызывать чего-то типа
TBindings.Notify('Property')


У Марко Канту в видео про LB есть такой примерчик. Попробовал использовать это дело для работы с опциями программы: сохранение/загрузка/показ на форме. В итоге для всех трех операций потребовалось что-то около 20 строчек кода :)
ter
19.05.2012 в 12:28
я с ними что то до сих пор не разобрался (:
Bonart
25.05.2012 в 17:46
Мда, чего только любители глобальных переменных не придумают.
А совсем без глобалов не пробовали? Намного проще и полезнее.
teran
25.05.2012 в 20:15
уважаемый, вы разницу между "как можно поступить" и "я так делаю, и всем советую" видите?
Проще и полезней, никто не спорит. И я нигде не писал, что люблю глобальные переменные. И даже наоборот написал в самом первом абзаце что это зло, от которого надо избавляться.
Артём
28.05.2012 в 15:20

У Александра Алексеева aka GunSmoker не читали http://www.gunsmoker.ru/2011/04/blog-post.html

teran
28.05.2012 в 17:49

читал (:

Akella
15.10.2012 в 16:45
добавить толко одну строку
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно