Страница 1 из 2
Singleton как реализовать?
Добавлено: 06.11.2015 08:23:17
xterro
Доброго времени суток, озадчился я значит созданием синглтона, чтобы в любом месте программы мог бы сделать что-то типа:
но ничего не выходит, немного кода:
Код: Выделить всё
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
Как же всё таки запилить синглтон?

Re: Singleton как реализовать?
Добавлено: 06.11.2015 08:56:57
zub
Это лишнее - переменная типа класс уже указатель, не надо разводить масло масляное.
Если всетаки есть необходимость делать через указательк, то надо еще подправить так:
Код: Выделить всё
if(app = nil) then begin
app := tapplication.create();
end;
result := @app;
Ну и GetInstance сделать классовым, как в вики - тогда всё заработает
Re: Singleton как реализовать?
Добавлено: 06.11.2015 11:16:19
xterro
Т.е переменную достаточно объявить так:
а функцию вот так:
Код: Выделить всё
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;
Т.е как я понял, в паскале нельзя такой указатель хранить в самом классе, а он должен быть опредлен только в самом модуле

Re: Singleton как реализовать?
Добавлено: 06.11.2015 11:34:20
zub
>>P.S. переделал так, заработало:
укакзатель на указатель на данные класса, хотя вполне достаточно иметь только один указатель))
>>Интересно, что даёт этот "class" и зачем тогда модификатор "static"?
Я как то не задумывался в чем разница - не использую. по сути одно и тоже. Надо эксперементально выяснять, подозреваю что в одном случае доступа к self не будет, в другом он всетаки будет.
>>Т.е как я понял, в паскале нельзя такой указатель хранить в самом классе, а он должен быть опредлен только в самом модуле

Какой указатель? если имеешь ввиду
то думаю его можно засунуть внутрь класса в виде классовой переменной
Re: Singleton как реализовать?
Добавлено: 06.11.2015 11:45:07
xterro
Всё, теперь понял, сделал всё по фен-шую с
tapplication, спасибо

Re: Singleton как реализовать?
Добавлено: 07.11.2015 16:38:15
Mirage
Прежде всего: Singleton - антипаттерн.
Проблем с ними много. В частности, приведенный выше вариант не рабочий в многопоточном окружении.
В FPC есть штатная замена - юниты.
Можно объявить переменную и инициализировать ее в соотв. секции. И даже корректно уничтожить.
Re: Singleton как реализовать?
Добавлено: 07.11.2015 18:22:06
xterro
Mirage писал(а):Прежде всего: Singleton - антипаттерн.
Проблем с ними много. В частности, приведенный выше вариант не рабочий в многопоточном окружении.
В FPC есть штатная замена - юниты.
Можно объявить переменную и инициализировать ее в соотв. секции. И даже корректно уничтожить.
Можно поподробнее про это? Я запускал несколько копий своего приложения, как раз с этим своим классом TApplication, всё работало нормально. Добавил в класс целочисленное поле ID, в конструкторе его инициализировал единицей и при вызове get_instance выводил это значение в консоль. Запустил неколько копий программы из разных консолей, везде вывелось 1. Вроде всё в порядке

Re: Singleton как реализовать?
Добавлено: 08.11.2015 01:08:12
Mirage
Многопоточность это не когда запускается несколько копий приложения, а когда в рамках одного приложения запускается несколько потоков.
И тестами корректность многопоточных программ проверить очень сложно. Тут речь только о какой-то вероятности корректной работы.
Т.е. если тест работает это еще не означает корректности программы.
Re: Singleton как реализовать?
Добавлено: 08.11.2015 01:34:30
zub
Mirage
А в чем проблема? если начинка класса который ТС пихает в синглтон не потокобезопасна - то и реализация этого безобразия в виде юнита или какимто другим способом будет также не безопасна (т.е. там где нужна потокобезопасность - нужны спецмеры, автоматически ее не добится). Это просто синтаксис и дело привычки - как легче воспринимается исходник, а суть одна и таже
Re: Singleton как реализовать?
Добавлено: 08.11.2015 03:14:39
Mirage
zub: Проблема в данном случае в "ленивой" инициализации экземпляра-синглтона. Если одновременно из разных потоков запросить экземпляр, то будет создано несколько.
Если же инициализировать экземпляр в секции инициализации юнита, то он всегда готов к работе.
Re: Singleton как реализовать?
Добавлено: 08.11.2015 11:38:30
xterro
Т.е если я где-нибудь в коде, сделаю новый поток(скажем создам в новом потоке диалог) в котором вызову:
Код: Выделить всё
app := tapplication.get_instance();
app.log.write('dialog created');
Будут проблемы, с чем отни связаны? Как избежать этой ситуации, может вместо статической функции просто передавать указатель на экземляр класса во все "внутренние" классы(в конструкторах этих классов)?
Например, tapplication отвечает за окно приложения, он содержит в себе переменную-член типа tcore, которая отвечает за всё содержимое окна. Правильнее будет в конструкторе tcore просто определить параметр типа tapplication и передавать указатель на него в tcore

Re: Singleton как реализовать?
Добавлено: 08.11.2015 23:00:35
Mirage
xterro писал(а):Будут проблемы, с чем отни связаны?
Связаны с тем, что в момент вызова get_instance() одним потоком, в другом потоке уже мог быть создан экземпляр, но еще не присвоен переменной.
xterro писал(а):Как избежать этой ситуации, может вместо статической функции просто передавать указатель на экземляр класса во все "внутренние" классы(в конструкторах этих классов)?
Как вариант. Можно, как я уже говорил, инициализировать сразу, в секции initialization.
Можно защитить get_instance() критической секцией, что негативно отразится на производительности при частом использовании.
Можно использовать сам юнит в качестве синглтона, вместо экземпляра класса.
Re: Singleton как реализовать?
Добавлено: 09.11.2015 00:02:56
zub
xterro
>>Как избежать этой ситуации, может вместо статической функции просто передавать указатель на экземляр класса во все
Избегать этой ситуации надо если ты пишешь многопоточное приложение. если нет или вообще незнаешь что это такое - пока не заморачивайся.
Mirage
>>Если же инициализировать экземпляр в секции инициализации юнита, то он всегда готов к работе.
Вовсе нет, инициализация "барахла" в initialization не отменяет необходимость критической секции для многопоточного приложения и проверок на то что "барахло" уже создано. Ничто не мешает другому потоку или просто другому юниту в своей секции инициализации запросить "барахло" раньше чем оно инициализировано в соответствующем юните. Это обычная ситуация если злоупотреблять uses в секции implementation, т.е. при циклических зависимостях модулей - в таких случаях взгляды на порядок инициализации юнитов со стороны компилятора (или разных компиляторов) и со стороны програмиста могут сильно расходиться))
Тут конечно можно поспорить - циклические зависимости зло и т.д... Но факт - initialization+finalization в общем случае не замена синглтонам и не панацея при многопоточности.
Re: Singleton как реализовать?
Добавлено: 10.11.2015 00:37:41
Mirage
zub: initialization+finalization не замена синглтонам, а один из способов реализации.
Думается, тут достаточно не использовать uses в секции implementation в юните, где объявлен синглтон. И можно иметь быстрый доступ к нему. По-моему достоинства перевешивают небольшое ограничение. Тем более, что циклические зависимости - зло.

А панацей при многопоточности не изобрели пока.
Re: Singleton как реализовать?
Добавлено: 10.11.2015 01:02:30
zub
>>Думается, тут достаточно не использовать uses в секции implementation в юните, где объявлен синглтон
не проверял, но думаю цепочку можно закрутить и сторонними юнитами, не трогая "синглтонный"
ТСу теперь есть из чего выбрать))