Singleton как реализовать?

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Singleton как реализовать?

Сообщение xterro » 06.11.2015 09:23:17

Доброго времени суток, озадчился я значит созданием синглтона, чтобы в любом месте программы мог бы сделать что-то типа:
Код: Выделить всё
app := tapplication.get_instance();

но ничего не выходит, немного кода:

Код: Выделить всё
unit tr_application;

interface
   
    uses
      //. . .

    type 
        papplication = ^tapplication;
       
        tapplication = class
        private
            fwindow_name    : string;
            fwindow_width   : integer;
            fwindow_height  : integer;
            ffullscreen     : boolean;
            fparams         : tstringList;
           
        public
           
            property window_width : integer  read fwindow_width  write fwindow_width;
            property window_height : integer read fwindow_height write fwindow_height;
       
            constructor create();
            destructor  destroy();
            function    get_instance() : papplication; static;
            . . .
        private
           
        end;
   
       
implementation   
   
    { Где объявлять переменную для хранения ссылки на экземпляр класса(на созданный объект)? }   
    var
        app : papplication = nil;
   
    constructor tapplication.create();
    begin
        fwindow_name    := 'tr_application_empty';
        fwindow_width   := 800;
        fwindow_height  := 600;
        ffullscreen     := false;
    end;
   
    function tapplication.get_instance() : papplication;
    begin
        if(app = nil) then begin
            app := papplication(tapplication.create());
        end;
        result := app;
    end;

    . . .


Использую это хозяйство так:

Код: Выделить всё

program ttt;
uses
    tr_application;

var
    app : papplication;
begin
    app := tapplication.get_instance();
    app^.create_window('', 1024, 768, false);
    app^.main_loop();
   
    app^.destroy();
end.


Сложность вызывает момент непосредственно создания объекта, где хранить ссылку на экземпляр класса? Например, в С++ можно хранить ссылку прямо в переменной класса, объявив её сатической, можно ли так же сделать в паскале, или нужно в каком-то блоке var это делать? На wiki так и сделано(http://wiki.freepascal.org/Singleton_Pattern), но в моём случае, компилятор ругается на app := tapplication.get_instance():

Error: Only class methods, class properties and class variables can be referred with class reference


Как же всё таки запилить синглтон? :?
xterro
постоялец
 
Сообщения: 148
Зарегистрирован: 23.02.2014 13:49:33

Re: Singleton как реализовать?

Сообщение zub » 06.11.2015 09:56:57

Код: Выделить всё
    type 
        papplication = ^tapplication;

Это лишнее - переменная типа класс уже указатель, не надо разводить масло масляное.
Если всетаки есть необходимость делать через указательк, то надо еще подправить так:
Код: Выделить всё
        if(app = nil) then begin
            app := tapplication.create();
        end;
        result := @app;


Ну и GetInstance сделать классовым, как в вики - тогда всё заработает
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Singleton как реализовать?

Сообщение xterro » 06.11.2015 12:07:04

Т.е переменную достаточно объявить так:

Код: Выделить всё
var
    app : tapplication = nil;

а функцию вот так:
Код: Выделить всё
class function  get_instance() : tapplication;

?
Интересно, что даёт этот "class" и зачем тогда модификатор "static"?

Добавлено спустя 9 минут 15 секунд:
P.S. переделал так, заработало:

Код: Выделить всё
var
    app : papplication;
begin
    app := tapplication.get_instance();
    app^.create_window('', 1024, 768, false);
    app^.main_loop();
   
    app^.destroy();
end.


Код: Выделить всё
type 
        papplication = ^tapplication;
       
        tapplication = class
        private
            . . .   
        public
            class function  get_instance() : papplication;

implementation
var
        app : tapplication = nil;
   
    constructor tapplication.create();
    begin
        fwindow_name    := 'tr_application_empty';
        fwindow_width   := 800;
        fwindow_height  := 600;
        ffullscreen     := false;
    end;
   

   
    class function tapplication.get_instance() : papplication;
    begin
        if(app = nil) then begin
            app := tapplication.create();
        end;
        result := @app;
    end;


Т.е как я понял, в паскале нельзя такой указатель хранить в самом классе, а он должен быть опредлен только в самом модуле :)
xterro
постоялец
 
Сообщения: 148
Зарегистрирован: 23.02.2014 13:49:33

Re: Singleton как реализовать?

Сообщение zub » 06.11.2015 12:34:20

>>P.S. переделал так, заработало:
укакзатель на указатель на данные класса, хотя вполне достаточно иметь только один указатель))

>>Интересно, что даёт этот "class" и зачем тогда модификатор "static"?
Я как то не задумывался в чем разница - не использую. по сути одно и тоже. Надо эксперементально выяснять, подозреваю что в одном случае доступа к self не будет, в другом он всетаки будет.

>>Т.е как я понял, в паскале нельзя такой указатель хранить в самом классе, а он должен быть опредлен только в самом модуле :)
Какой указатель? если имеешь ввиду
Код: Выделить всё
var
    app : papplication;

то думаю его можно засунуть внутрь класса в виде классовой переменной
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Singleton как реализовать?

Сообщение xterro » 06.11.2015 12:45:07

Всё, теперь понял, сделал всё по фен-шую с tapplication, спасибо :)
xterro
постоялец
 
Сообщения: 148
Зарегистрирован: 23.02.2014 13:49:33

Re: Singleton как реализовать?

Сообщение Mirage » 07.11.2015 17:38:15

Прежде всего: Singleton - антипаттерн.
Проблем с ними много. В частности, приведенный выше вариант не рабочий в многопоточном окружении.
В FPC есть штатная замена - юниты.
Можно объявить переменную и инициализировать ее в соотв. секции. И даже корректно уничтожить.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Singleton как реализовать?

Сообщение xterro » 07.11.2015 19:22:06

Mirage писал(а):Прежде всего: Singleton - антипаттерн.
Проблем с ними много. В частности, приведенный выше вариант не рабочий в многопоточном окружении.
В FPC есть штатная замена - юниты.
Можно объявить переменную и инициализировать ее в соотв. секции. И даже корректно уничтожить.

Можно поподробнее про это? Я запускал несколько копий своего приложения, как раз с этим своим классом TApplication, всё работало нормально. Добавил в класс целочисленное поле ID, в конструкторе его инициализировал единицей и при вызове get_instance выводил это значение в консоль. Запустил неколько копий программы из разных консолей, везде вывелось 1. Вроде всё в порядке :(
xterro
постоялец
 
Сообщения: 148
Зарегистрирован: 23.02.2014 13:49:33

Re: Singleton как реализовать?

Сообщение Mirage » 08.11.2015 02:08:12

Многопоточность это не когда запускается несколько копий приложения, а когда в рамках одного приложения запускается несколько потоков.
И тестами корректность многопоточных программ проверить очень сложно. Тут речь только о какой-то вероятности корректной работы.
Т.е. если тест работает это еще не означает корректности программы.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Singleton как реализовать?

Сообщение zub » 08.11.2015 02:34:30

Mirage
А в чем проблема? если начинка класса который ТС пихает в синглтон не потокобезопасна - то и реализация этого безобразия в виде юнита или какимто другим способом будет также не безопасна (т.е. там где нужна потокобезопасность - нужны спецмеры, автоматически ее не добится). Это просто синтаксис и дело привычки - как легче воспринимается исходник, а суть одна и таже
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Singleton как реализовать?

Сообщение Mirage » 08.11.2015 04:14:39

zub: Проблема в данном случае в "ленивой" инициализации экземпляра-синглтона. Если одновременно из разных потоков запросить экземпляр, то будет создано несколько.
Если же инициализировать экземпляр в секции инициализации юнита, то он всегда готов к работе.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Singleton как реализовать?

Сообщение xterro » 08.11.2015 12:38:30

Т.е если я где-нибудь в коде, сделаю новый поток(скажем создам в новом потоке диалог) в котором вызову:
Код: Выделить всё
app := tapplication.get_instance();
app.log.write('dialog created');

Будут проблемы, с чем отни связаны? Как избежать этой ситуации, может вместо статической функции просто передавать указатель на экземляр класса во все "внутренние" классы(в конструкторах этих классов)?
Например, tapplication отвечает за окно приложения, он содержит в себе переменную-член типа tcore, которая отвечает за всё содержимое окна. Правильнее будет в конструкторе tcore просто определить параметр типа tapplication и передавать указатель на него в tcore :?
xterro
постоялец
 
Сообщения: 148
Зарегистрирован: 23.02.2014 13:49:33

Re: Singleton как реализовать?

Сообщение Mirage » 09.11.2015 00:00:35

xterro писал(а):Будут проблемы, с чем отни связаны?


Связаны с тем, что в момент вызова get_instance() одним потоком, в другом потоке уже мог быть создан экземпляр, но еще не присвоен переменной.

xterro писал(а):Как избежать этой ситуации, может вместо статической функции просто передавать указатель на экземляр класса во все "внутренние" классы(в конструкторах этих классов)?


Как вариант. Можно, как я уже говорил, инициализировать сразу, в секции initialization.
Можно защитить get_instance() критической секцией, что негативно отразится на производительности при частом использовании.
Можно использовать сам юнит в качестве синглтона, вместо экземпляра класса.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Singleton как реализовать?

Сообщение zub » 09.11.2015 01:02:56

xterro
>>Как избежать этой ситуации, может вместо статической функции просто передавать указатель на экземляр класса во все
Избегать этой ситуации надо если ты пишешь многопоточное приложение. если нет или вообще незнаешь что это такое - пока не заморачивайся.

Mirage
>>Если же инициализировать экземпляр в секции инициализации юнита, то он всегда готов к работе.
Вовсе нет, инициализация "барахла" в initialization не отменяет необходимость критической секции для многопоточного приложения и проверок на то что "барахло" уже создано. Ничто не мешает другому потоку или просто другому юниту в своей секции инициализации запросить "барахло" раньше чем оно инициализировано в соответствующем юните. Это обычная ситуация если злоупотреблять uses в секции implementation, т.е. при циклических зависимостях модулей - в таких случаях взгляды на порядок инициализации юнитов со стороны компилятора (или разных компиляторов) и со стороны програмиста могут сильно расходиться))
Тут конечно можно поспорить - циклические зависимости зло и т.д... Но факт - initialization+finalization в общем случае не замена синглтонам и не панацея при многопоточности.
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Singleton как реализовать?

Сообщение Mirage » 10.11.2015 01:37:41

zub: initialization+finalization не замена синглтонам, а один из способов реализации.
Думается, тут достаточно не использовать uses в секции implementation в юните, где объявлен синглтон. И можно иметь быстрый доступ к нему. По-моему достоинства перевешивают небольшое ограничение. Тем более, что циклические зависимости - зло.:)
А панацей при многопоточности не изобрели пока.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Singleton как реализовать?

Сообщение zub » 10.11.2015 02:02:30

>>Думается, тут достаточно не использовать uses в секции implementation в юните, где объявлен синглтон
не проверял, но думаю цепочку можно закрутить и сторонними юнитами, не трогая "синглтонный"
ТСу теперь есть из чего выбрать))
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

След.

Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 16

Рейтинг@Mail.ru
cron