Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2013.10.06;
Скачать: [xml.tar.bz2];

Вниз

Нужен третейский судья :)   Найти похожие ветки 

 
Rouse_ ©   (2013-04-22 20:55) [0]

Итак спор:

есть отдельный не связанный с базовым проектом юнит, который может использоваться в множестве проектов.
В юните реализован некий класс TFoo

оппонент предлагает решение в виде:

{$IFDEF SELF_INITIALIZE}
var
 Foo: TFoo = nil;
{$ENDIF}

...

initialization
{$IFDEF SELF_INITIALIZE}
 Foo := TFoo.Create;
{$ENDIF}

finalization
 FreeAndNil(Foo);


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

function Foo: TFoo;

implementation

var
 _Foo: TFoo = nil;

function Foo: TFoo;
begin
 if _Foo = nil then
   _Foo := TFoo.Create;
 Result := _Foo;
end;

...

initialization

finalization
 FreeAndNil(Foo);


Объясняю оппоненту - в данном случае не нужно заморачиваться с перекомпиляцией, тем более в случае использования юнита в рамках нескольких проектов.
Оппонент отвечает тем, что в случае реализации расширенного класса TFoo2 = class(TFoo) в модуле Foo2, ему достаточно не объявлять директиву SELF_INITIALIZE и тогда он может объявить очередную глобальную переменную вида
var
 Foo: TFoo2 = nil;

и с этим будет более удобно работать.

Я отвечаю что это не верная идеология разработки, ибо глобальная Foo в данный момент уже зарезервированна в конкретном модуле, как и сам класс TFoo, который тоже нельзя переобъявить в новом юните, ибо он уже обьявлен.

Оппонент отвечает что моя идеология не верна, ибо нельзя объявлять синглтон на требуемую переменную :)

В итоге уперлись :)

Рассудите, плз, ибо толь я уже не умею объяснять как правильно, или я соовсем отстал :)


 
___   (2013-04-22 21:05) [1]

Удалено модератором


 
DevilDevil ©   (2013-04-22 21:10) [2]

Розыч, не стоит отталкиваться от мнения "если это принято, значит это правильно"
В данном случае подойдёт сравнение плюсов и минусов

Плюс "синглетона":
 - доступно из коробки

Минусы "синглетона":
 - возможный конфликт имён
 - функция вместо переменной

Плюс дифайна:
 - доступно из коробки + опциональность (может кому-то не нужен глобальный объект Foo)
 
Минус дифайна:
 - нужно задать дифайн SELF_INITIALIZE в опциях проекта

По моему в данном случае дифайн победитель


 
картман ©   (2013-04-22 21:12) [3]

только монетка


 
имя   (2013-04-22 21:15) [4]

Удалено модератором


 
Rouse_ ©   (2013-04-22 21:18) [5]


> Минусы "синглетона":
>  - возможный конфликт имён
>  - функция вместо переменной

Это не минусы, но плюсы, да и как можно на конфликт то выйти?


> По моему в данном случае дифайн победитель

Имхо наоборот.

А из плюсов - использование модуля в 10 проектах к примеру, под каждый рекомпил делать, чтоб директива скушалась?


 
Ega23 ©   (2013-04-22 21:20) [6]

Удалено модератором


 
Rouse_ ©   (2013-04-22 21:21) [7]


> Ega23 ©   (22.04.13 21:20) [6]

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


 
Rouse_ ©   (2013-04-22 21:23) [8]


> DevilDevil ©   (22.04.13 21:10) [2]

Зы, Дим, ты кстати аська выйди-то, раз на мыло не реагируешь, а то ты мне еще лично персонально нужен :)


 
Ega23 ©   (2013-04-22 21:26) [9]


> Там у тебя проблема была с тем что класс настроек был синглтоном


Не только, там ещё с лицензированием что-то было.
А, впрочем, фиг с ним.

По-сабжу:
есть третье решение:

unit FooUnit;

type

 TFoo = class
   .....
 end;

end.

unit SingletonFooUnit;

function Foo: TFoo;
....

end.


 
картман ©   (2013-04-22 21:31) [10]


> тем более что под тебя спецом все переделали.

Free вызывал постоянно?))


 
DVM ©   (2013-04-22 21:36) [11]

Я за второй вариант


 
Ega23 ©   (2013-04-22 21:37) [12]


> Free вызывал постоянно?))


Не, там просто дофига всего тянулось ненужного


 
oxffff ©   (2013-04-22 21:42) [13]

А постановку задачи можно? Может все проще можно сделать.

P.S. Представьте себе структуру на Java(а поскольку в java нет структур, то это класс), смысл в которой это набор данных. Я объявил как набор public полей.

Мой код увидел разработчик на Java и обернул это все сеттерами и геттерами (напрямую работающие с каждым полем) и свел поля в private. Я его спросил, а зачем? Мне ответили бо инкапсуляция.


 
oxffff ©   (2013-04-22 21:46) [14]

По сабжу мне не нравится ни тот ни другой вариант.
Переменную состояние нужно как минимум внести в класс как статическую(class var), чтобы не захламлять namespace


 
oxffff ©   (2013-04-22 21:53) [15]


> есть отдельный не связанный с базовым проектом юнит, который
> может использоваться в множестве проектов.
> В юните реализован некий класс TFoo


1. А зачем для этих целей вообще создавать объект?

2. Быть может все можно решить статическими методами класса?


 
oxffff ©   (2013-04-22 22:04) [16]


> Переменную состояние нужно как минимум внести в класс как
> статическую(class var), чтобы не захламлять namespace


в смысле class function TFoo.getInstance();static;

Но все же [15].


 
vuk ©   (2013-04-22 22:12) [17]

Как вариант - методы класса + переменная типа class of TFoo и работать через эту переменную. Тогда можно будет подменить реализацию, буде оно понадобится.


 
картман ©   (2013-04-22 22:15) [18]

ну, тогда кубик


 
Rouse_ ©   (2013-04-22 22:21) [19]


> oxffff ©   (22.04.13 21:53) [15]
> 1. А зачем для этих целей вообще создавать объект?

Допустим это класс, работающий с файлом в блокирующем режиме, в статике хэндл хранить негде будет (ну если не переоткрывать его каждый раз или не использовать глобалку, но тогда зачем вообще синглтон?).


 
oxffff ©   (2013-04-22 22:26) [20]


> Допустим это класс, работающий с файлом в блокирующем режиме,
>  в статике хэндл хранить негде будет


Привет, Саша.
Так есть статические переменные класса. Зачем выделять память в куче, если  по сути это класс утилит, который подсоединяется по запросу с использованием  uses. То есть насколько я понял использование класса, как namespace, для группировки методов.

Но!!!
Все же давай с постановки задачи, а то так можно далеко уйти.


 
oxffff ©   (2013-04-22 22:32) [21]


> но тогда зачем вообще синглтон


Я то мат. часть читал, да вот из паттернов использую "Посетитель".
И то уже не считаю паттерны как панацею.
А больше стал склоняться к функциональному программированию.


 
Дмитрий С ©   (2013-04-22 22:39) [22]

Я думаю задача - побольше кофе попить, да покурить :)

С директивами вариант сложнее для понимания. К тому же поддерживать класс который может быть синглетоном самоинициализирющимся, а может быть и нет - тоже себе задачка.


> var
>  _Foo: TFoo = nil;

Подчеркивание перед именем - бррр. Это этот класс синглетон, я бы его переменную сделал бы классовой (или статической, не знаю как правильно называется).

Кстати, а есть смысл инлайнить функцию Foo?


 
DVM ©   (2013-04-22 22:55) [23]

Директивы условной компиляции без крайней необходимости - это вообще зло. С ними зачастую очень сложно понять что происходит а коде, потому как тут читаем тут не читаем, а вон в тот кусочек в отдельном инклюде рыбу заворачивали. Здесь они не нужны.
Сам я чаще практиковал второй вариант, но в последнее время стал избегать глобальных переменных и обычных (не классовых и не методов) функций, стараясь все вносить внутрь классов.


 
DevilDevil ©   (2013-04-22 23:07) [24]

> Rouse_ ©   (22.04.13 20:55)

Поймал себя на мысли, что если бы я проектировал библиотеку, в которой должен быть один глобальный объект - я бы просто объявил глобальную переменную как Application: TApplication и создавал/удалял бы в initialization/finalization

Практика применения функций-синглетонов у меня небольшая. Но возможно это где-то оправдано

Если я правильно представляю себе библиотеку, о которой ты говоришь :), то использование дифайна там обусловлено опциональным использованием глобального объекта (специфика проекта). Я уверен, если бы не было требования опциональности, автор просто бы сделал глобальную переменную без всяких синглетонов


 
Rouse_ ©   (2013-04-22 23:36) [25]


> oxffff ©   (22.04.13 22:26) [20]

Пфф, в аську стучись - а то всю интригу раскрою :)


 
Rouse_ ©   (2013-04-22 23:37) [26]


> DVM ©   (22.04.13 22:55) [23]
> Директивы условной компиляции без крайней необходимости
> - это вообще зло

О!!! Об чем и речь...


 
Rouse_ ©   (2013-04-22 23:41) [27]


> DevilDevil ©   (22.04.13 23:07) [24]

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


 
DevilDevil ©   (2013-04-23 00:12) [28]

> Rouse_ ©   (22.04.13 23:41) [27]

ну... я как-то не выдаю dcu фрилансерам :)
по поводу рекомпиляции - есть отличный пункт меню "Build"
очень помогает :)


 
Германн ©   (2013-04-23 02:13) [29]


> по поводу рекомпиляции - есть отличный пункт меню "Build"

Так о нем и шла речь. Перекомпилировать модуль при изменении только Conditional Defins иначе никак не возможно. Но эта невозможность однозначно приводит к тому, что билдить нужно заново весь проект. Что создаёт некие неудобства.


 
Макс Черных   (2013-04-23 02:18) [30]


> Оппонент отвечает что моя идеология не верна, ибо нельзя
> объявлять синглтон на требуемую переменную :)

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


 
DevilDevil ©   (2013-04-23 02:35) [31]

> Германн ©   (23.04.13 02:13) [29]

как часто ты перекомпилируешь разные проекты ?
лично у меня правило когда открываю проект - перебилдить его. Чтобы не использовать кешированые dcu, которые были созданы с неизвестными опциями компилятора и дифанами. Потом при работе над проектом просто его компилируешь

я не знаю что вы бучу подняли
для меня обращение к экземпляру каждый раз по функции страшнее


 
Германн ©   (2013-04-23 02:35) [32]


> Rouse_ ©   (22.04.13 20:55)  

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


 
Германн ©   (2013-04-23 02:43) [33]


> DevilDevil ©   (23.04.13 02:35) [31]
>
> > Германн ©   (23.04.13 02:13) [29]
>
> как часто ты перекомпилируешь разные проекты ?

Пересобираю проекты (вроде именно так принято переводить на русский Build Projects) постоянно. Сейчас уже не часто, ибо те проекты сейчас уже редко изменяются. Но если изменяются, то тогда пересобираю все шесть. :)


 
Германн ©   (2013-04-23 02:56) [34]


> Rouse_ ©   (22.04.13 23:36) [25]
>
>
> > oxffff ©   (22.04.13 22:26) [20]
>
> Пфф, в аську стучись - а то всю интригу раскрою :)

А нафига тогда было нужно задавать вопрос тут?


 
Юрий Зотов ©   (2013-04-23 05:02) [35]

Я бы использовал синглтон, только не в такой редакции. В такой редакции это не синглтон, поскольку никто не мешает вызвать конструктор напрямую и насоздавать кучу экземпляров. Чтобы этого не было, надо перекрыть NewInstance и FreeInstance. И создавать экземпляр синглтона нужно при первом обращении к нему, а не в initialization.


 
Ega23 ©   (2013-04-23 08:14) [36]


> как часто ты перекомпилируешь разные проекты ?

Раз в сутки это билд-сервер делает. Как минимум.


 
Kilkennycat ©   (2013-04-23 08:19) [37]

Я за более простое решение Розыча, ибо исчо Оккам запрещал плодить сущности без необходимости.


 
sniknik ©   (2013-04-23 08:45) [38]

делал так, с условной компиляцией для общего модуля для клиента и сервера.... глупо пытался "минимизировать объем программы", чтобы в сервере не создавалось визуальных объектов которые все одно там не используются.
в итоге уже через неделю сам путался где, что и какая версия скомпилированного лежит (когда пишешь клиент сервер открыты оба проекта), + возникали непонятные глюки, приходилось билдить чуть ли не каждый раз.
в начале второй плюнул и разнес проверку на нил и создание объектов по функциям, т.е. "а ля второе решение" только не в инициализации. смысл - в сервисе функция не вызывается лишнего обьекта не создается, не вызываемые функции оптимизатор выкидывает сам... путаницы нет.

резюме - если твой оппонент будет сам пользоваться юнитом минимум в паре проектов одновременно с разными "настройками", то через некоторое время поймет "проблему", если же чисто теоретизирования, или использует в 1м то не испытав неудобств (один раз сбилдить не проблема) то останется при своем мнению что бы ты не говорил (и мы).


 
sniknik ©   (2013-04-23 08:50) [39]

> что в случае реализации расширенного класса TFoo2 = class(TFoo) в модуле Foo2
а он не ставит себе перед сном стакан воды рядом? ну, в случае если ночью проснется и захочет пить.
(© идея из какого то анекдота)


 
Dimka Maslov ©   (2013-04-23 09:07) [40]

Я всегда пользуюсь вторым вариантом, и только исходя из принципа "объект должен создаваться в тот момент, когда он впервые понадобился". Это значительно ускоряет начальную загрузку приложения, особенно в случаях, когда таких глобальных объектов множество и они потребляют значительные ресурсы. Что же касается соображения "функция вместо переменной", тот тут могут возникнуть некоторые сложности при отладке, не более того.


 
oxffff ©   (2013-04-23 09:13) [41]


> Rouse_ ©   (22.04.13 23:36) [25]
>
> > oxffff ©   (22.04.13 22:26) [20]
>
> Пфф, в аську стучись - а то всю интригу раскрою :)


Саша, аськанамана нету. Есть скайпанамана.
append 81 к нику.


 
Компромисс1 ©   (2013-04-23 09:52) [42]

Я вообще не вижу в чем проблема, ведь Delphi позволяет использовать полное имя (с именем модуля) для переменной.
Пишите
fooUnit.foo для оригинальной переменной и foo2unit.foo, если будет переменная foo, которая будет иметь класс Foo2.
Вот тут очень хорошо заметно, в чем преимущество Java - она заставляет писать имя "файла", Delphi-программисты слишком ленивы.

Первый вариант все же лучше, потому что при втором возможен одновременный вызов функции foo из двух потоков, тогда получим гонку.


> Мой код увидел разработчик на Java и обернул это все сеттерами
> и геттерами (напрямую работающие с каждым полем) и свел
> поля в private. Я его спросил, а зачем? Мне ответили бо
> инкапсуляция.


Если класс public, то он прав - лишишься возможности поменять реализацию (вдруг надо будет добавить какую-то проверку на права, например). Если же private, то можно и так оставить, для быстродействия.


 
Компромисс1 ©   (2013-04-23 09:57) [43]


> Я бы использовал синглтон, только не в такой редакции. В
> такой редакции это не синглтон, поскольку никто не мешает
> вызвать конструктор напрямую и насоздавать кучу экземпляров.
>  Чтобы этого не было, надо перекрыть NewInstance и FreeInstance.
>  И создавать экземпляр синглтона нужно при первом обращении
> к нему, а не в initialization.


При таком подходе будет невозможно создать потомка. Бывает, что нужен singleton предка и singleton потомка.


 
DevilDevil ©   (2013-04-23 10:01) [44]

Ребят, вы все всё путаете
Синглетоны, функции, классовые переменные и прочее...
Как я уже говорил, согласно специфике проекта, нахождение этого "синглетона" опционально. Например есть серверная библиотека, которая по идее может работать с несколькими клиентами. Но существуют задачи, когда предполагается взаимодействие одного лишь клиента с сервером. Для таких случаев библиотека предоставляет особый вариант компиляции.

Т.е. в данном случае, используя "синглетон" Розыча, получилось бы так:

{$IFDEF SELF_INITIALIZE}
function Foo: TFoo;
{$ENDIF}

implementation

{$IFDEF SELF_INITIALIZE}
var
_Foo: TFoo = nil;

function Foo: TFoo;
begin
if _Foo = nil then
  _Foo := TFoo.Create;
Result := _Foo;
end;
{$ENDIF}

...

initialization

finalization
 {$IFDEF SELF_INITIALIZE}
FreeAndNil(Foo);

 {$ENDIF}


 
Компромисс1 ©   (2013-04-23 10:15) [45]


> Но существуют задачи, когда предполагается взаимодействие
> одного лишь клиента с сервером.


Существуют задачи, когда клиент или сервер можен в runtime менять стратегию, поэтому перекомпиляция зло.


 
DevilDevil ©   (2013-04-23 10:19) [46]

> Компромисс1 ©   (23.04.13 10:15) [45]

> Существуют задачи, когда клиент или сервер можен в runtime
> менять стратегию, поэтому перекомпиляция зло.


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


 
DevilDevil ©   (2013-04-23 10:24) [47]

> перекомпиляция зло.

те многочисленные С++ проекты которые я видел, пестрят списком дифайнов, которыми можно задавать режим работы библиотеки. Видимо С++ программисты не знают, что перекомпиляция зло. Особенно это смешно на контрасте скорости компиляции Delphi-проектов и С++-проектов


 
Rouse_ ©   (2013-04-23 10:29) [48]


> DevilDevil ©   (23.04.13 10:01) [44]
> Т.е. в данном случае, используя "синглетон" Розыча, получилось
> бы так:

Не, в данном случае использование директив не нужно.
В случае их использования и глобальной переменной в памяти всегда висит созданный обьект - нужен он или нет.
В случае синглтона, он создается по факту обращения к нему.
Это принципиальная разница.


 
Ega23 ©   (2013-04-23 10:32) [49]

Есть некий класс с каким-то там функционалом. Допустим, он работает только в рамках какого-то одного проекта. Допустим, логика проекта такова, что он должен существовать в одном экземпляре. Вот тогда (и, ИМХО, и только тогда) синглтон (в любых его реализациях) имеет право на существование.

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

Появился третий проект, серия юнит-тестов данного класса. На разные тест-кейсы в SignUp должен создаваться экземпляр данного класса (пусть единственный), а на TearDowm - гарантированно прибиваться.

Вопрос: нахрена в юните с описанием данного класса городить какие-то глобальные переменные под директивами или лепить функции-синглтоны?


 
Rouse_ ©   (2013-04-23 10:43) [50]


> Ega23 ©   (23.04.13 10:32) [49]

Синглтон в данном случае ничем не мешает.


 
Юрий Зотов ©   (2013-04-23 10:52) [51]


> Компромисс1 ©   (23.04.13 09:57) [43]
> При таком подходе будет невозможно создать потомка.

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

unit Unit2;

interface

type
 TSingleton1 = class
 public
   class function NewInstance: TObject; override;
   procedure FreeInstance; override;
 end;

 TSingleton2 = class(TSingleton1)
 public
   class function NewInstance: TObject; override;
   procedure FreeInstance; override;
 end;

 function Singleton1: TSingleton1;
 function Singleton2: TSingleton2;

implementation

var
 _Singleton1: TObject = nil;
 _Singleton2: TObject = nil;

{ TSingleton1 }

function Singleton1: TSingleton1;
begin
 if _Singleton1 = nil then
   _Singleton1 := TSingleton1.Create;
 Result := TSingleton1(_Singleton1)
end;

procedure TSingleton1.FreeInstance;
begin
 inherited;
 _Singleton1 := nil
end;

class function TSingleton1.NewInstance: TObject;
begin
 if _Singleton1 = nil then
   _Singleton1 := inherited NewInstance;
 Result := _Singleton1
end;

{ TSingleton2 }

function Singleton2: TSingleton2;
begin
 if _Singleton2 = nil then
   _Singleton2 := TSingleton2.Create;
 Result := TSingleton2(_Singleton2)
end;

procedure TSingleton2.FreeInstance;
begin
 inherited;
 _Singleton2 := nil
end;

class function TSingleton2.NewInstance: TObject;
var
 P: Pointer;
begin
 if _Singleton2 = nil then
 begin
   GetMem(P, InstanceSize);
   try
     _Singleton2 := InitInstance(P)
   except
     FreeMem(P, InstanceSize);
     raise
   end
 end;
 Result := _Singleton2
end;

end.


PS

Надеюсь, реализация метода TSingleton2.NewInstance развеет все сомнения по поводу нашей недавней дискуссии - стоит ли давать программисту возможность прямой работы с памятью, или не стоит.

Спички и ножи надо прятать от детей, а для взрослого человека они в миллион раз более полезны, чем опасны. А бывает даже и так, что без них и не прожить просто.


 
Pit   (2013-04-23 10:58) [52]


>
> var
>  _Foo: TFoo = nil;

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


 
Ega23 ©   (2013-04-23 10:59) [53]


> Синглтон в данном случае ничем не мешает.

Но и не полезен ничем. А раз так, то нафига козе баян?


 
Компромисс1 ©   (2013-04-23 11:01) [54]

Ega23 ©   (23.04.13 10:32) [49]

+1.


 
Юрий Зотов ©   (2013-04-23 11:10) [55]

Небольшая поправка к [51]. Хотя работает и без нее, но с нею лучше.

procedure TSingleton2.FreeInstance;
begin
 CleanupInstance;
 FreeMem(Pointer(Self), InstanceSize);
 _Singleton2 := nil
end;


 
Компромисс1 ©   (2013-04-23 11:12) [56]

Юрий Зотов ©   (23.04.13 10:52) [51]

Это, конечно, замечательно, но я нигде не вижу, чтобы из Singleton2 вызывался конструктор (NewInstance) Singleton1. А потом будем удивляться, почему код, добавленный Singleton1.NewInstance, не срабатывает для Singleton2. А как же наследование?


> Надеюсь, реализация метода TSingleton2.NewInstance развеет
> все сомнения по поводу нашей недавней дискуссии - стоит
> ли давать программисту возможность прямой работы с памятью,
>  или не стоит.


Отнюдь. Ведь может оказаться, что у нас даже нет исходников Singleton1, когда мы пишем Singleton2. Как же убедиться, что мы не пропустили ничего важного, что было в Singleton1.NewInstance? Конечно, мы можем надеяться, что в Singleton1.NewInstance нет ничего важного, потому что опытный разработчик этого класса вынес все важное в новый (желательно virtual) метод DoInit, но я бы на это не надеялся :)

Кстати, указанная проблема есть не только в Delphi, поэтому с некоторых пор я считаю singleton антипаттерном. Сервис типа SingletonFactory.getInstance() был бы гораздо лучше, особенно учитывая, что он может вернуть вовсе не Singleton1, а какой-нибудь Singleton2. А Singleton2Factory.getInstance()  - и вовсе Singleton359 :)


 
Romkin ©   (2013-04-23 11:13) [57]

Что тут спорить? уже сколько лет есть нормальный класс, он же и является синглетоном. Делайте конструктор класса, и все получится. Это оно и есть, синглетон :)


 
Владислав ©   (2013-04-23 11:13) [58]

var
 _Foo: TFoo = nil;


:= nil во всех вариантах лишнее.

А по сути любой вариант, кроме первого. В первом варианте объявление переменной внутри {$IFDEF SELF_INITIALIZE}. Это при каждом обращении к ней писать IFDEF? А что писать, если SELF_INITIALIZE не объявлено?
Я делал так, как в [51], и как в варианте с функцией, в зависимости от ситуации.


 
DVM ©   (2013-04-23 11:14) [59]


> Юрий Зотов ©   (23.04.13 11:10) [55]

Единственный недостаток - не потокобезопасно. Interlocked функции надо применить.


 
Компромисс1 ©   (2013-04-23 11:14) [60]


> Небольшая поправка к [51]. Хотя работает и без нее, но с
> нею лучше.


Надеюсь, реализация этого метода развеет все сомнения по поводу нашей недавней дискуссии - стоит ли давать программисту возможность ошибиться (или неоптимально сделать), или не стоит.

:)


 
DevilDevil ©   (2013-04-23 11:16) [61]

> Rouse_ ©   (23.04.13 10:43) [50]
>
> > Ega23 ©   (23.04.13 10:32) [49]
> Синглтон в данном случае ничем не мешает.


он мешает своим присутствием


 
DevilDevil ©   (2013-04-23 11:23) [62]

> Юрий Зотов ©   (23.04.13 10:52) [51]

а какой практический смысл перехвата NewInstance?
если избежать повторного выделения памяти - то получается не очень хорошо. Ибо Вы не вызываете InitInstance. А если вызовете - то возможны утечки памяти (если в экземпляре были строки/варианты/интерфейсы/массивы/сложные структуры)


 
Romkin ©   (2013-04-23 11:25) [63]


> Единственный недостаток - не потокобезопасно. Interlocked
> функции надо применить.

Любой синглетон изначально небезопасен.


 
Компромисс1 ©   (2013-04-23 11:28) [64]


> Любой синглетон изначально небезопасен.
>


Точно? Неужели нет безопасного способа на Delphi? :(

The Java programming language solutions provided here are all thread-safe but differ in supported language versions and lazy-loading. Since Java 5.0, the easiest way to create a Singleton is the enum type approach, given at the end of this section.

http://en.wikipedia.org/wiki/Singleton_pattern


 
DVM ©   (2013-04-23 11:30) [65]


> Romkin ©   (23.04.13 11:25) [63]


> Любой синглетон изначально небезопасен.

Что значит изначально? Все зависит от того, как написать его реализацию.
Опасное место всегда одно и то же - проверка наличия экземпляра - создание экземпляра, все остальное вторично.


 
DVM ©   (2013-04-23 11:32) [66]


> Компромисс1 ©   (23.04.13 11:28) [64]


> Точно? Неужели нет безопасного способа на Delphi? :(

Есть конечно, посмотрите например класс TEncoding как там создаются стандартные кодировки, которые и есть синглтоны по сути.


 
DVM ©   (2013-04-23 11:33) [67]


> DVM ©   (23.04.13 11:32) [66]

class function TEncoding.GetUTF8: TEncoding;
var
 LEncoding: TEncoding;
begin
 if FUTF8Encoding = nil then
 begin
   LEncoding := TUTF8Encoding.Create;
   if InterlockedCompareExchangePointer(Pointer(FUTF8Encoding), LEncoding, nil) <> nil then
     LEncoding.Free;
 end;
 Result := FUTF8Encoding;
end;


 
Компромисс1 ©   (2013-04-23 11:35) [68]

DVM ©   (23.04.13 11:33) [67]

Спасибо.


 
Romkin ©   (2013-04-23 11:38) [69]


> > Точно? Неужели нет безопасного способа на Delphi? :(Есть
> конечно, посмотрите например класс TEncoding как там создаются
> стандартные кодировки, которые и есть синглтоны по сути.
>

Я имел в виду что надо "ручками" защищать. А Java нам не указ, в Parallel Fortran, если память не изменяет, вообще можно цикл по потокам распихать.


 
_oxffff   (2013-04-23 11:41) [70]

И уносит кого и уносит кого в блестящую снежную даль.....


 
Romkin ©   (2013-04-23 11:43) [71]

Во-во. Уже несется. Отсюда простой вердикт: оба способа в корзину, делайте класс и не спорьте :)


 
Компромисс1 ©   (2013-04-23 11:48) [72]


> Я имел в виду что надо "ручками" защищать. А Java нам не
> указ, в Parallel Fortran, если память не изменяет, вообще
> можно цикл по потокам распихать.
>


Давайте еще GPSS вспомним :)
Извините, если что, я просто на Delphi и Java примерно одинаковое время работал, поэтому обычно их и сравниваю.


 
Romkin ©   (2013-04-23 12:40) [73]

Во, нашел демонстрашку синглетона на 2010.

type
 EManagerError = class(Exception);

 TFoo = class(TObject)
 public
   class function GetName: string; virtual;
   constructor Create; virtual;
 end;

 TFooClass = class of TFoo;

 TFooManager = class
 strict private
   class var FFooList: TDictionary<string, TFooClass>;
   class destructor Done;
 public
   class function RegisterFoo(AFoo: TFooClass; const Key: string): boolean;
   class function GetFoo(Key: string): TFooClass;
   class function CreateFoo(Key: string): TFoo;

 end;


 
Компромисс1 ©   (2013-04-23 12:42) [74]

Это мультитон, не синлтон. Для каждого key свой экземпляр.


 
Ega23 ©   (2013-04-23 12:53) [75]

Где-ж это синглтон-то? кто мне мешает стопицот экземпляров создать?


 
_oxffff   (2013-04-23 13:02) [76]

три белых коня ....


 
брат Птибурдукова   (2013-04-23 13:07) [77]

Саня, в твоей статье на хабре СТОЛЬКО опечаток… :-(


 
Romkin ©   (2013-04-23 13:11) [78]


> Где-ж это синглтон-то? кто мне мешает стопицот экземпляров
> создать?

TFooManager - синглетон. Ну можешь развлечься созданием экземпляров, конструктор в приват я не убирал. Но это ничего не изменит :)


 
Компромисс1 ©   (2013-04-23 13:16) [79]

Romkin ©   (23.04.13 13:11) [78]

В таком случае любой класс, в котором только class functions и нет instance functions, можно назвать singleton. Хотя можно назвать и utility class, если даже создавать экземпляры необязательно :)


 
Romkin ©   (2013-04-23 13:22) [80]


> В таком случае любой класс, в котором только class functions
> и нет instance functions, можно назвать singleton. Хотя
> можно назвать и utility class, если даже создавать экземпляры
> необязательно :)

Естественно. Если что-то ведет себя как синглетон, то это синглетон и есть :)


 
Юрий Зотов ©   (2013-04-23 13:37) [81]

> Компромисс1

Согласен, реализация кривая - а что Вы хотите от кода, написанного наскоро за 5 минут? Вечером постараюсь сделать нормально.

Однако, несмотря ни на что, факт остается фактом - лишение программиста возможности работать с памятью напрямую ограничивает его. Например, в части работы с системным API.

> DevilDevil ©   (23.04.13 11:23) [62]

> а какой практический смысл перехвата NewInstance?
????????????? Мы хотим сделать синглтон, или что?

> Ибо Вы не вызываете InitInstance.
???????????? А что же там написано, если не вызов InitInstance?

> А если вызовете - то возможны утечки памяти (если в экземпляре были
> строки/варианты/интерфейсы/массивы/сложные структуры)
??????????? Однако, VCL вызывает и ничего страшного не происходит. См. модуль System.


 
Компромисс1 ©   (2013-04-23 13:40) [82]


> Согласен, реализация кривая - а что Вы хотите от кода, написанного
> наскоро за 5 минут? Вечером постараюсь сделать нормально.
>


Не надо. Если Вам самому это неинтересно. Меня всё равно не переубедить )


> Однако, несмотря ни на что, факт остается фактом - лишение
> программиста возможности работать с памятью напрямую ограничивает
> его. Например, в части работы с системным API.


Согласен. Но обычно не просто так ограничивают, а предоставляют альтернативные интсрументы. Вот того же goto в Java нет и вряд ли когда-то будет. Хотя да, можно назвать лишением возможности.


 
Pit   (2013-04-23 14:21) [83]


> Вот того же goto в Java нет и вряд ли когда-то будет. Хотя
> да, можно назвать лишением возможности.

ну дык правильно. Один из немногочисленных вариантов, когда goto полезен - выход из вложенных циклов. А тут у java есть для этого спец. конструкция.
Убрали goto - избавились от минусов и плюсов его, а плюсы реализовали по-другому. Хорошее решение, что сказать то.


 
Pit   (2013-04-23 14:22) [84]

Розыч!! Объясни мне, юродивому, нафига в твоих примерах в начале везде глобальная переменная ссылочного типа инициализируется nil? Они же по-умолчанию nil, неужели в какой-то дельфе это отменили?


 
Pit   (2013-04-23 14:22) [85]


> лишение программиста возможности работать с памятью напрямую
> ограничивает его

в ошибках.


 
Inovet ©   (2013-04-23 14:25) [86]

> [84] Pit   (23.04.13 14:22)
> Они же по-умолчанию nil, неужели в какой-то дельфе это отменили?

Вдруг в какой-то версии nil перестанет быть равным 0. Шутка типа.


 
Компромисс1 ©   (2013-04-23 14:27) [87]


> Розыч!! Объясни мне, юродивому, нафига в твоих примерах
> в начале везде глобальная переменная ссылочного типа инициализируется
> nil? Они же по-умолчанию nil, неужели в какой-то дельфе
> это отменили?


Это имеет смысл для визуализации того, что в секции initialization нет присвоения.

var a:Object = nil;
var b:Object;

initialization
 b = foo();


 
DevilDevil ©   (2013-04-23 14:31) [88]

> Romkin ©   (23.04.13 13:11) [78]

есть нормальный класс TFoo
как ты объявишь синглетон Foo: TFoo своим методом ?


 
DevilDevil ©   (2013-04-23 14:40) [89]

> Юрий Зотов ©   (23.04.13 13:37) [81]

нет, всё-таки не понятно, зачем 2 разные функции синглетона
зачем перехватывать NewInstance
и чем это лучше простой реализации Розыча


 
Ega23 ©   (2013-04-23 14:57) [90]


> и чем это лучше простой реализации Розыча


function Foo: TFoo;

implementation

var
 _Foo: TFoo;

function Foo: TFoo;
begin
 if _Foo = nil then
   _Foo := TFoo.Create;
 Result := _Foo;
end;

finalization
 _Foo.Free;

В другом юните
 Foo.Free;

В лучшем случае - Invalid Pointer Operation на выходе из программы.


 
Romkin ©   (2013-04-23 15:00) [91]


> есть нормальный класс TFooкак ты объявишь синглетон Foo:
>  TFoo своим методом ?

Вот зачем - в голову не приходит. Синглетоны создаются специально. Ты хочешь получить именно синглетон-потомок TFoo? Это подозрительно.


 
DevilDevil ©   (2013-04-23 15:02) [92]

> Romkin ©   (23.04.13 15:00) [91]

> Вот зачем - в голову не приходит. Синглетоны создаются специально.
>  Ты хочешь получить именно синглетон-потомок TFoo? Это подозрительно.


это не я хочу
это Розыч хочет


 
DevilDevil ©   (2013-04-23 15:05) [93]

> Ega23 ©   (23.04.13 14:57) [90]

ну а так лучше что ли ?

S1 := TSingleton2.Create;
S2 := TSingleton2.Create;

S2.Free;
S1.Free;


 
Юрий Зотов ©   (2013-04-23 15:10) [94]

> DevilDevil ©   (23.04.13 14:40) [89]

> зачем перехватывать NewInstance
Синглтон не должен позволять создание более одного своего экземпляра.

> и чем это лучше простой реализации Розыча
Кто помешает 100 раз вызвать конструктор и создать 100 экземпляров?


 
Romkin ©   (2013-04-23 15:15) [95]


> это не я хочу это Розыч хочет

Розыч, кажется, ничего подобного не заявлял :)
Хочется потомка существующего класса как синглетон, то на мой взгляд грамотно написать синглетон (там глобальные данные, отличные от данных экземпляра должны быть) и адаптер к нему для приведения к нужному типу.
Так на мой взгляд грамотнее, поскольку отделяется логика синглетона от использования экземпляра класса.


 
DevilDevil ©   (2013-04-23 15:16) [96]

> Юрий Зотов ©   (23.04.13 15:10) [94]

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


тогда логичнее выкидывать Exception при конструкторе (NewInstance) если экземпляр уже существует

> Кто помешает 100 раз вызвать конструктор и создать 100 экземпляров?
ну во-первых, такая возможность у TFoo как раз и должна быть
во-вторых, как я показал в [93], Ваш метод должен выдать AccessViolation


 
Ega23 ©   (2013-04-23 15:19) [97]

Да достали уже, ей-Б-гу. Ну вот вас ни разу не смущает, что главная форма, фактически, работает как синглтон. Хотя - глобальная переменная. Хотя - можно насоздавать стопицот экземпляров.
Однако же
var
 frmMain: TfrmMain;
И ничо, всё работает.


 
DevilDevil ©   (2013-04-23 15:20) [98]

> Romkin ©   (23.04.13 15:15) [95]

это я всё к чему говорю:
1) если класс изначально создаётся как класс в единственном экземпляре, то твой подход оправдан. TFoo - это другой случай. Когда вполне может быть много экземпляров
2) исходя из моего понимания синглетонов от какого-то класса - реализация Розыча средствами Delphi самая оптимальная
3) специфику библиотеки и почему оправдано только использование дифайна - я тоже объяснить

об чём спор то тогда ?


 
DevilDevil ©   (2013-04-23 15:21) [99]

> Ega23 ©   (23.04.13 15:19) [97]

она работает как "синглетон" только в том случае если она явно инициализируется
в типичном понимании Application.CreateForm(TfrmMain, frmMain);


 
Rouse_ ©   (2013-04-23 15:22) [100]

Я немного поясню что именно я хотел и откуда вообще все вылезло:

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

Если бы небыло этой инициализации класса внутри самого юнита - то и спора никакого небыло бы :)
Тем более 99.9 юнитов не использует никакой инициализатор, просто реализация класса и все.


 
DevilDevil ©   (2013-04-23 15:25) [101]

> Rouse_ ©   (23.04.13 15:22) [100]

розыч, ты автор 100го сообщения
а я тебе в сотый раз говорю, что предназначение дифайна - это не замена функции
var
_Foo: TFoo = nil;

function Foo: TFoo;
begin
if _Foo = nil then
  _Foo := TFoo.Create;
Result := _Foo;
end;


Предназначение дифайна - определить частный режим работы библиотеки
Это выглядит как переменная. На самом деле это режим работы, подразумевающий определённый регламент


 
Romkin ©   (2013-04-23 15:28) [102]


> 1) если класс изначально создаётся как класс в единственном
> экземпляре, то твой подход оправдан. TFoo - это другой случай.
>  Когда вполне может быть много экземпляров

Вот все что я видел - методы и данные синглетона специфичны. То есть он пишется от какого-то достаточно абстрактного класса, со своими данными и методами, отличающимися от остальных потомков.


 
Romkin ©   (2013-04-23 15:29) [103]


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

Замените на конструктор класса.


 
DevilDevil ©   (2013-04-23 15:32) [104]

> Romkin ©   (23.04.13 15:29) [103]

см [101]


 
Romkin ©   (2013-04-23 15:41) [105]


> см [101]

см. [103] :)


 
Ega23 ©   (2013-04-23 15:44) [106]


> см [101]


> см. [103] :)


см. [106]


 
Ega23 ©   (2013-04-23 15:44) [107]

Я победил. :)


 
DevilDevil ©   (2013-04-23 15:45) [108]

нет я :)


 
_oxffff   (2013-04-23 15:50) [109]


> Замените на конструктор класса.


+1.


 
_oxffff   (2013-04-23 15:56) [110]


> Invalid Pointer Operation


А мужики то не в курсе.
The Delphi Language for Mobile Development
http://www.delphifeeds.com/go/s/103218

AutomaticReferenceCounting (Delphi XE4)

http://docwiki.embarcadero.com/RADStudio/XE4/en/Automatic_Reference_Counting_in_Delphi_Mobile_Compilers

Сам читаю пока не совсем понял, что насчет циклических зависимостей.


 
Rouse_ ©   (2013-04-23 16:17) [111]


> Предназначение дифайна - определить частный режим работы
> библиотеки
> Это выглядит как переменная. На самом деле это режим работы,
>  подразумевающий определённый регламент

Господи, и чем ему синглтон не угодил? © Почти Барон Мюнгхаузен :)


 
antonn ©   (2013-04-23 16:32) [112]


> P.S. Представьте себе структуру на Java(а поскольку в java
> нет структур, то это класс), смысл в которой это набор данных.
>  Я объявил как набор public полей.
>
> Мой код увидел разработчик на Java и обернул это все сеттерами
> и геттерами (напрямую работающие с каждым полем) и свел
> поля в private. Я его спросил, а зачем? Мне ответили бо
> инкапсуляция.

я частенько так на шарпе делаю если только код не "на отшибись".. как правило если вдруг потом нужно выполнить какие нибудь действия перед "назначением значения" - то все уже готово :)


 
DevilDevil ©   (2013-04-23 16:34) [113]

> Rouse_ ©   (23.04.13 16:17) [111]

Розыч, я не знаю, понимаешь ли ты или нет
Тут вопрос идеологии
Фундамент так сказать всего и вся

Камазу нужно 6 колёс
Мотоциклу с люлькой - 3
Ты же предлагаешь делать 4,5 колеса. Который и для мотоцикла не подходит, и для камаза не подходит.

дифайн - это режим работы


 
Компромисс1 ©   (2013-04-23 16:37) [114]

DevilDevil ©   (23.04.13 16:34) [113]

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


 
Ega23 ©   (2013-04-23 16:50) [115]


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


Не, ну если под библиотекой понимать отдельную сущность, типа dll - то ограничивается.
А если как набор dcu - тады ой.


 
DevilDevil ©   (2013-04-23 17:11) [116]

> Компромисс1 ©   (23.04.13 16:37) [114]

очевидно я об этом знаю :)))))


 
DevilDevil ©   (2013-04-23 17:17) [117]

> Ega23 ©   (23.04.13 16:50) [115]

а что с dcu ?
как надо - так и компилируй
нужно dcu с синглетоном - скомпилируй и отдавай
нужно dcu без синглетона - компилируй и отдавай

я не понимаю в чём тут сложность
наоборот дифайны в данном случае лучшие друзья
потому что если синглетон нафиг не спёрся, а в модуле уже объявлен как у Розыча - тут уже могут быть проблемы

p.s.
кстати у меня уже была ситуация, когда нужно было обращаться к Foo: TFoo. И в паре мест предполагалось брать этот Foo как аргумент функции. Но в процессе рефакторинга, я этот параметр добавить забыл - и функция обращалась к глобальной переменной Foo, что привело к багу, который я потом долго искал. В общем конфликт имён - зло редкое


 
han_malign   (2013-04-23 17:17) [118]


> Господи, и чем ему синглтон не угодил?

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


 
Ega23 ©   (2013-04-23 17:30) [119]


> я не понимаю в чём тут сложность


Сложность тут в том, что у меня, например, уже есть дефайн, с которым собраны твои dcu. И завязан он на обращение к астралу 29 февраля каждого високосного года.


 
DevilDevil ©   (2013-04-23 17:42) [120]

> Ega23 ©   (23.04.13 17:30) [119]

когда ты компилируешь pas в dcu в x86 и x64 - тебя почему-то не смущает, что на выходе получаются 2 совершенно разных dcu
когда ты компилируешь под разными версиями Delphi - ты почему то тоже не удивляешься, что на выходе получаются разные dcu
более того, разная комбинация опций компилятора делают разные dcu - и это тоже никого не парит

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


 
_oxffff   (2013-04-23 17:45) [121]


> дифайн - это режим работы


Вот так и рождаются слухи.


 
Ega23 ©   (2013-04-23 17:45) [122]

Vox populi vox Dei.
Селяви.


 
DevilDevil ©   (2013-04-23 17:59) [123]

> _oxffff   (23.04.13 17:45) [121]

> Вот так и рождаются слухи.

какие ваши доказательства ?


 
картман ©   (2013-04-23 19:30) [124]

ну, так чего там - с тупого или острого?


 
oxffff ©   (2013-04-23 19:43) [125]


> DevilDevil ©   (23.04.13 17:59) [123]
> > _oxffff   (23.04.13 17:45) [121]
>
> > Вот так и рождаются слухи.
>
> какие ваши доказательства ?


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


> дифайн - это режим работы


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

Ты видел  где-нибудь программы, которые
могут изменять свое состояние с помощью дефайнов в процессе работы?


 
DevilDevil ©   (2013-04-23 20:33) [126]

> oxffff ©   (23.04.13 19:43) [125]

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


 
Ega23 ©   (2013-04-23 22:30) [127]


> прости, но я не уловил логику
> ты уверен, что она действительно есть ?


Наверное, ты ещё слишком молод и не знаешь, что такое KOKANHYM


 
Владислав ©   (2013-04-23 22:52) [128]

Если бы небыло этой инициализации класса внутри самого юнита - то и спора никакого небыло бы :)
Обманул в изначальном вопросе. :)


 
Аббат Пиккола   (2013-04-23 22:59) [129]

Несколько предложений.
1. Вообще выбросить нафиг эту переменную. Обойтись без нее вообще. Если кому-то нужна, пусть у себя объявит. Класс есть. ИМХО, этого за глаза достаточно.
2. Если уж обязательно нужна глобальная переменная в одном месте, то  разговоры о классе TFoo2 требуют какого-то механизма, который определял бы класс этой глобальной переменной. Наверно можноо создать еще одну глобальную переменную, в котороую будет записываться ссылка на нужный класс. А функция Foo пусть создает экземпляр указанного класса как-нибудь. Я это не умею, но думаю, что это возможно.

Хотя мне это все не нравится. Если возможны какие-то наследники от TFoo, то что произойдет, если кто-то вздумает иметь 2 разных класса-наследника? Инициализироваться-то будет в любом случае экземпляр какого-то конкретного класса, так как объект - одиночка. Другой класс окажется обиженным в такой ситуации. Точно так же обиженным, как TFoo2 в случае функции Foo.

Я бы послал вообще эту глобальную переменную. Если двое спорят о том, как это реализовать и каждый подход имеет свои недостатки, может лучше вообще от нее избавиться и дело с концом? Т.е. я за мой вариант 1. Лучший способ решить проблему - ликвидировать саму проблему. Кому нужно пусть У СЕБЯ объявляет глобальную переменную и делает с ней, что хочет.


 
sniknik ©   (2013-04-24 08:19) [130]

> Лучший способ решить проблему - ликвидировать саму проблему.
а она ликвидируется?
стоят двое на остановке спорят
- по моему, туда где мы вчера бухали, не помню куда, нужно ехать на 5-ом автобусе, конечная остановка...
- а по моему, тоже не помню, на 7-м, тоже конечная...
...
- ликвидируйте проблему, езжайте на такси!
- ???

> Кому нужно пусть У СЕБЯ объявляет глобальную переменную и делает с ней, что хочет.
зачем останавливаться на полпути? кому нужно тот пусть пишет свой класс/объект и делает с ним что хочет.
только вот это уже не будет общий модуль, с общей логикой, а будет куча частных реализаций. при изначальном - нужен синглетон. или не нужен... если хочется второй экземпляр.
у автора в общем то спор, а не вопрос из-за не знания как сделать. думаю он и сам способен придумать кучу извращенных вариантов. но чем это кого то убедит?


 
oxffff ©   (2013-04-24 09:08) [131]


> sniknik ©   (24.04.13 08:19) [130]


:)
+10.


 
Ega23 ©   (2013-04-24 09:23) [132]


> зачем останавливаться на полпути? кому нужно тот пусть пишет
> свой класс/объект и делает с ним что хочет.


Во! Точняк!


 
DevilDevil ©   (2013-04-24 09:35) [133]

Господа, вы акцентируете внимание на реализации
Не задумываясь, что нужно получить в итоге


 
Юрий Зотов ©   (2013-04-24 09:36) [134]

Вот, набросал потокобезопасный синглтон (возможно, неоптимальный - желающие могут улучшить).

Позволяет создать лишь один экземпляр своего класса. И любой отнаследованный от него класс тоже автоматически становится синглтоном (если, конечно, программист сам себе не враг и не изуродовал его поведение, не вызвав inherited в перекрытых им же NewInstance и FreeInstanse).

unit Unit2;

interface

type
 TSingleton = class
 public
   class function NewInstance: TObject; override;
   procedure FreeInstance; override;
 end;

implementation

uses
 Classes, SysUtils;

var
 SingletonList: TThreadList;

function IndexOfSingleton(SingletonClass: TClass): integer;
var
 i: integer;
begin
 Result := -1;
 with SingletonList, LockList do
 try
   for i := 0 to Count - 1 do
     if TObject(Items[i]).ClassType = SingletonClass then
     begin
       Result := i;
       Exit
     end
 finally
   UnlockList
 end
end;

{ TSingleton }

procedure TSingleton.FreeInstance;
var
 i: integer;
begin
 with SingletonList, LockList do
 try
   i := IndexOf(Self);
   if i < 0 then
     Exit;
   inherited;
   Delete(i);
 finally
   UnlockList
 end
end;

class function TSingleton.NewInstance: TObject;
var
 i: integer;
begin
 with SingletonList, LockList do
 try
   i := IndexOfSingleton(Self);
   if i < 0 then
   begin
     Result := inherited NewInstance;
     Add(Result)
   end
   else
     Result := Items[i]
 finally
   UnlockList
 end
end;

initialization

 SingletonList := TThreadList.Create;

finalization

 SingletonList.Free

end.


 
DevilDevil ©   (2013-04-24 10:11) [135]

> Юрий Зотов ©   (24.04.13 09:36) [134]

при всём уважении, но

type
 TMySingleton = class(TSingleton)
    Name: string;
 end;

...
var
 S1, S2: TMySingleton;
begin
 S1 := TMySingleton.Create;
 S2 := TMySingleton.Create;

 S1.Name := "Делай раз";
 S1.Free;

 S2.Name := "Делай два"; // AV
 S2.Free;
end;


Если честно, я так и не понял цимус такого класса-синглетона


 
Компромисс1 ©   (2013-04-24 10:35) [136]

Кстати, тут weak reference напрашивается.


 
Аббат Пиккола   (2013-04-24 11:19) [137]

sniknik ©   (24.04.13 08:19) [130]

> Лучший способ решить проблему - ликвидировать саму проблему.
а она ликвидируется?
стоят двое на остановке спорят
- по моему, туда где мы вчера бухали, не помню куда, нужно ехать на 5-ом автобусе, конечная остановка...
- а по моему, тоже не помню, на 7-м, тоже конечная...
...
- ликвидируйте проблему, езжайте на такси!
- ???


Если один из спорящих помнит, что "вчера бухали в ресторане с каким-то птичьим названием", то таксист может сказать "господа, это ресторан "Орлы", это конечная 7-го автобуса!". Сами же спорящие, опираясь лишь на эту информацию, задачу не решат. Хотя и могут придумать кучу извращенных вариантов типа "сядем на что попало, какая разница, где сегодня бухать".

Я многократно обнаруживал одну странную вещь: если мне не нравится ни один из вариантов, что я рассматриваю, значит точно существует лучшее решение. Более того, я его уже сам нашел, но почему-то не осознаю. Отказавшись на какое-то мгновение решать задачу как таковую, я всегда это решение находил. Так как это решение всегда ищется в иной постановке задачи. Поэтому я и предлагаю абсурдный путь - отказаться от решения этой задачи в таком виде. Чтобы освободить мозг от всего вороха аргументов, которые в данном случае не убеждают.

Если же цель в том, чтобы просто набрать сторонников в пользу своей точки зрения, тогда прошу меня простить - это психологическая игра под названием "А почему бы не...? Да, но", в которой побеждает затеявший ее, когда у остальных игроков просто не останется уже никаких идей.


 
Компромисс1 ©   (2013-04-24 11:22) [138]

Варианты:
1) Я умру молодым
2) Я умру немолодым
Мне не нравится ни один из вариантов, значит Бог есть и я буду жить вечно :)


 
Ega23 ©   (2013-04-24 11:32) [139]


> Мне не нравится ни один из вариантов, значит Бог есть и
> я буду жить вечно :)


Так они и проповедуют бессмертие души. если чо.


 
sniknik ©   (2013-04-24 11:40) [140]

> опираясь лишь на эту информацию, задачу не решат.
да боже ж мой... решение, опираясь лишь на эту - каждый едет по "своему" маршруту, на конечной созваниваются. = либо кто то прав, либо оба не правы. с точки зрения спора оба варианта его (спор) решают (а цель по топику в этом, вовсе не поиске конкретного места).

применительно к теме решение (предлагал выше), суть - дайте спорящему самому поработать на паре проектов одновременно, и активно, чтобы часто, и каждый раз переключаясь нужно было "билдить". через неделю не убедится, сам не поменяет, и не наделает ошибок/путаницы (не то отправит) из-за этого значит прав.


 
DevilDevil ©   (2013-04-24 11:43) [141]

> Поэтому я и предлагаю абсурдный путь - отказаться от решения
> этой задачи в таком виде. Чтобы освободить мозг от всего
> вороха аргументов, которые в данном случае не убеждают.


Задача - иметь 2 разных режима работы библиотеки
Первый режим универсальный, второй режим - синглетон конкретного типа с конкретным именем
Найди решение лучше, чем использовать дифайны


 
Компромисс1 ©   (2013-04-24 11:49) [142]


> Так они и проповедуют бессмертие души. если чо.


Я знаю. Всегда было интересно, как к религии приходят умные вроде бы люди. Вот оно как, Петрович (с)


 
Аббат Пиккола   (2013-04-24 11:50) [143]

Все зависит от практики будущего применения модуля. Согласны?
Если предполагается модуль продавать, то может оказаться, что исходный вариант с базовым классом TFoo устраивает 99% потребителей данного продукта. А для тех, кто недоволен "ограничениями", имеется define. что превращает их в "довольных", так как они не просто юзают этот модуль. но и могут творчески развить класс TFoo, получая свою зарплату незадаром. Все довольны.

А может быть ситуация иная. Базовый класс абстрактен или сильно урезан и предполагается, что в любом случае потребители будут создавать TFoo2. Но тогда спор бы и не возник вообще. Искали бы подходящую реализацию для этого случая.

Мне видится спор так. Автор полагает, что 100% потребителей будут удовлетворены  функциональностью TFoo. Его заботит лишь красота реализации. А его оппонент хочет оставить за потребителями иную возможность тоже. Мне кажется, что оппонент прав. Иначе спор бы и не возник вообще.

При всем моем искреннем уважении к автору вопроса.


 
sniknik ©   (2013-04-24 11:53) [144]

> Первый режим универсальный, второй режим - синглетон конкретного типа с конкретным именем
???
наоборот, второй универсальный. первый в зависимости от дефайна, конкретно частное решение.

вот что мешает во втором решении создать экземпляр прямо от типа не используя функцию с условием? да ничего. и ничего делать не надо чтобы из проекта в проект была единая логика модуля.


 
Ega23 ©   (2013-04-24 11:53) [145]


> Найди решение лучше, чем использовать дифайны


Создать файл с настройками и менять там значение.
Настройки должны быть на основе ini-файла, естественно.


 
sniknik ©   (2013-04-24 11:57) [146]

> А его оппонент хочет оставить за потребителями иную возможность тоже.
вообще то если нужно "тоже", то не нужен синглетон, если по логике проекта нужна единая "моно" реализация, то все эти инсинуации типа "а вдруг им захочется" нужно отсекать как вредительские. "закладывания" на такого типа желания только усложняют (добавляют ошибок в) проект.


 
Аббат Пиккола   (2013-04-24 11:59) [147]

2 Компромисс1 ©   (24.04.13 11:22) [138]

Варианты:
1) Я умру молодым
2) Я умру немолодым
Мне не нравится ни один из вариантов, значит Бог есть и я буду жить вечно :)


Есть еще решение. Мне не нравится ни один из вариантов, значит оставлю эту проблему Богу, а сам попробую беспокоиться не о смерти, а о жизни. Если вечная жизнь есть, зачем беспокоиться? Если же ее нет, тем более бессмысленно беспокоиться о чем-то кроме самой жизни.

Богословие напоминает доказательство от противного. Мы допускаем нечто невозможное, чтобы продемонстрировать логическое противоречие, к которому мы придем. Можно наброситься на доказывающего таким способом с обвинением "зачем предполагаешь невозможное, несчастный!" А можно проследить за ходом его мыслей.


 
sniknik ©   (2013-04-24 12:01) [148]

> Создать файл с настройками и менять там значение.
по "Розычу" там и настроек не нужно... просто ты либо используешь его функцию и объект создается, либо нет, тогда не создается, а нужна копия объекта делаешь ее сам... можно ссылаясь непосредственно на тип/класс... все "автоматом".


 
Аббат Пиккола   (2013-04-24 12:04) [149]

sniknik ©   (24.04.13 11:57) [146]

Как тогда форум может выступить в качестве третейского судьи?


 
DevilDevil ©   (2013-04-24 12:07) [150]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 12:11) [151]


> Есть еще решение. Мне не нравится ни один из вариантов,
> значит оставлю эту проблему Богу, а сам попробую беспокоиться
> не о смерти, а о жизни. Если вечная жизнь есть, зачем беспокоиться?
>  Если же ее нет, тем более бессмысленно беспокоиться о чем-
> то кроме самой жизни.


Нет, это не еще одно решение. Вариант "Если же ее нет, тем более бессмысленно беспокоиться о чем-то кроме самой жизни." тоже не нравится некоторым, можно сказать, что это разновидность варианта "Умру немолодым". Поэтому только Бог, и не спорьте, аббат :)


 
Компромисс1 ©   (2013-04-24 12:13) [152]


> Задача - иметь 2 разных режима работы библиотеки
> Первый режим универсальный, второй режим - синглетон конкретного
> типа с конкретным именем
> Найди решение лучше, чем использовать дифайны


Легко. Причем оно уже приводилось в этой ветке. Объявление синглтона в отдельном unit. Кому нужно, тот его подключает в uses.


 
Аббат Пиккола   (2013-04-24 12:16) [153]

2 sniknik ©   (24.04.13 12:01) [148]

Если не считать существенной экономией то, что объект в "реализации Розыча" может и не создаваться, а обратить внимание на другое - на то, что при такой реализации этом объект может быть только классом TFoo, но никак не его наследником, то и обнаруживается главная причина спора.

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

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

Конечно, можно это реализовать через ini, но все опять упрется в тот же вопрос. Нужно дать возможность расширять класс или не нужно.

Если Розыч так уверен, что это никогда не понадобится, чем его не устраивает директива компилятору? Суть ведь вовсе не в том, как реализовать саму инициализацию. Через функцию или тупо создав объект. Тоже мне экономия... Суть спора в ином. Дать возможность изменить класс глобального объекта или не дать.

Поэтому я и предлагаю. Глобальный объект выбросить нафиг. Тогда сразу станет ясно, зачем он вообще нужен. Если вообще нужен...


 
DevilDevil ©   (2013-04-24 12:19) [154]

> Легко. Причем оно уже приводилось в этой ветке. Объявление
> синглтона в отдельном unit. Кому нужно, тот его подключает
> в uses.


альтернатива
но в таком случае ты делаешь 2 модуля библиотеки вместо одного
по мне - удобнее дифайн


 
DevilDevil ©   (2013-04-24 12:20) [155]

ну и потом это первый (универсальный) вариант
в его рамках можно просто объявить переменную "синглетон" где-то у себя в проекте и всё


 
sniknik ©   (2013-04-24 12:23) [156]

> TFoo, но никак не его наследником
почему? вот допустим, внезапно понадобилось.... против логики модуля но вдруг, это проблема вынести описание типа в интерфейсную часть? да хоть изначально его там описать. не делается этого обычно лишь потому, что при синглетоне не должно быть копий.


 
Аббат Пиккола   (2013-04-24 12:38) [157]

Синглетон это такой религиозный ритуал? Или есть конкретное применение для глобальной переменной? Если она необходима, придется ее создавать в любом случае. Я бы за экономией места не гнался. Но вместо дифайна, требующего SELF_INITIALIZE я бы сделал негативный дифайн

DISABLE_SELF_INITIALIZE

Если этого дифайна нет, синглетон инициализируется объектом класса TFoo.
чтобы не принуждать никого упоминать SELF_INITIALIZE. Но допустить иное.


 
DevilDevil ©   (2013-04-24 12:39) [158]

> Аббат Пиккола   (24.04.13 12:38) [157]
> Но вместо дифайна, требующего SELF_INITIALIZE я бы сделал
> негативный дифайн DISABLE_SELF_INITIALIZE


я думал об этом
скорее всего так и будет в будущем :)


 
Компромисс1 ©   (2013-04-24 13:11) [159]


> альтернатива
> но в таком случае ты делаешь 2 модуля библиотеки вместо
> одного
> по мне - удобнее дифайн


И об этом тоже писали. Придется потом задействовать стороннюю библиотеку, в которой совершенно случайно используется точно такой же define, но с совершенно другой целью, и придется изменять код.
А еще писали, что когда в проекте много разных define, то тяжело ориентироваться.
У меня был опыт, когда функциональность собиралась в зависимости от наличия примерно 10 define (для разных клиентов), мало удовольствия, но там хотя бы смысл был- приложение нельзя было взломать и использовать неоплаченную функицональность. А тут вообще странно получается, давайте на каждую переменную по define заведем.


 
Юрий Зотов ©   (2013-04-24 13:59) [160]

> DevilDevil ©   (24.04.13 10:11) [135]

S1 := TMySingleton.Create;
S2 := TMySingleton.Create;

Итак, код сработал: S1 и S2 - это ссылки на один и тот же объект. Далее.

S1.Free;

Объект уничтожен и теперь любое обращение к нему (хоть через S1, хоть через S2) выдаст AV. Что и должно быть.

А Вы ожидали чудес? Ожидали, что обращение к уже уничтоженному объекту через переменную с другим именем будет каким-то волшебным образом работать без AV ?

Не будет.

> Если честно, я так и не понял цимус такого класса-синглетона

Если честно, то Вы не понимаете цимус синглтона ВООБЩЕ, а не только ТАКОГО синглтона. Синглтоны используются тогда, когда в программе не нужны несколько одновременно существующих объектов одного и того же класса. Например, это часто бывают какие-то фабрики. В IDE Delphi тоже есть синглтоны - инспектор объектов, палитра выравнивания и т.п.

Синглтон не обязательно оформлять именно как синглтон - часто бывает вполне достаточно того, что программист сам знает, что объект вот этого класса ему создавать не нужно (главная форма, например). Но если мы пишем какую-то библиотеку, где используется синглтон и хотим застраховать ее пользователя (то есть, прикладного программиста) от ошибок, то синглтон мы должны оформить именно как синглтон.

Вот такой цимус.


 
Компромисс1 ©   (2013-04-24 14:04) [161]

Юрий Зотов ©   (24.04.13 13:59) [160]

ИМХО Вы требуете от пользователя знания реализации Вашего TMySingleton для нормального использования.
Я бы либо не позволил создавать S2 := TMySingleton.Create совсем, либо изменил название метода на какой-нибудь getInstance. Create все-таки четко указывает, что создается новый объект.


 
_oxffff   (2013-04-24 14:06) [162]


> А Вы ожидали чудес? Ожидали, что обращение к уже уничтоженному
> объекту через переменную с другим именем будет каким-то
> волшебным образом работать без AV ?
>
> Не будет.


Чудеса будут в XE4 под мобильные компиляторы.

Раздел
The Free and DisposeOf methods under ARC

http://docwiki.embarcadero.com/RADStudio/XE4/en/Automatic_Reference_Counting_in_Delphi_Mobile_Compilers


 
euru ©   (2013-04-24 14:07) [163]

А как такой вариант:

unit Unit1;

interface

type
  ISingleton = interface
  end;

function Singleton: ISingleton;

implementation

type
  TSingleton = class(ISingleton)
  end;

var SingletoneInstance: TSingletone;

function Singletone: ISingletone;
begin
  if SingletoneInstance = nil then
    SingletoneInstance = TSingletone.Create;
  Result := SingletoneInstance;
end;

finalization
  if SingletoneInstance <> nil then
    SingletoneInstance.Free;
end.


 
DevilDevil ©   (2013-04-24 14:34) [164]

> Юрий Зотов ©   (24.04.13 13:59) [160]

это я к тому, что вообще не понятно, по кой фиг иметь возможность вызывать конструкторы по несколько раз для одного и того же объекта
а раз уж кому-то это понадобилось - то логично заложить адекватную реакцию на соответствующий Free, чего Вы не сделали. Реализуется это подсчётом ссылок. Но опять таки не понятно зачем


 
DevilDevil ©   (2013-04-24 14:40) [165]

Удалено модератором


 
DevilDevil ©   (2013-04-24 14:47) [166]

> Юрий Зотов ©   (24.04.13 13:59) [160]

и ещё момент

type
TMySingleton = class(TSingleton)
   Name: string;

   constructor Create;
end;

constructor TMySingleton.Create;
begin
 inherited;
 Name := "Default value";
end;

...
var
S1, S2: TMySingleton;
begin
S1 := TMySingleton.Create;

S1.Name := "задаём какое-то имя";
Writeln("Имя = ", S1.Name); // "задаём какое-то имя"

S2 := TMySingleton.Create;

Writeln("Имя = ", S1.Name); // "Default value"
end;


Как бы Вы не перехватили NewInstance - код конструктора всёравно вызовется


 
Компромисс1 ©   (2013-04-24 14:56) [167]

Удалено модератором


 
Ega23 ©   (2013-04-24 14:59) [168]

Удалено модератором


 
DevilDevil ©   (2013-04-24 14:59) [169]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 15:04) [170]

DevilDevil ©   (24.04.13 14:59) [169]

Так я ж написал, в чем минусы define по сравнению с дополнительным модулем. Теперь твоя очередь найти минусы в дополнительном модуле. Само наличие дополнительного модуля минусом не является, потому как не всеми он используется.


 
DevilDevil ©   (2013-04-24 15:05) [171]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 15:09) [172]

Удалено модератором


 
DevilDevil ©   (2013-04-24 15:14) [173]

> Так я ж написал, в чем минусы define по сравнению с дополнительным
> модулем


в моей жизни НИ РАЗУ не было, чтобы два дифайна пересеклись, ни разу
имена дифайнов специально выбирают такими, чтобы они были говорящими и уникальными

по поводу множества дифайнов - они наверное были в конфиге и подключались в хедере по {$include/$i}
тоже совершенно стандартный подход
что здесь может быть неприятно и почему это минус - я не понял

По поводу модуля с переменной - особого минуса нет
Но я считаю плохим тоном создавать модуль для 3х строк кода: переменной, конструктора и деструктора
Уж лучше нормально объявить переменную в рамках своего проекта и использовать
Выражаясь твоим языком - давайте для каждой переменной свой модуль заводить


 
_oxffff   (2013-04-24 15:15) [174]

Кокаинум.....


 
DevilDevil ©   (2013-04-24 15:16) [175]

Удалено модератором
Примечание: Читаем правила пожалуйста в течение недели


 
Компромисс1 ©   (2013-04-24 15:18) [176]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 15:20) [177]

Удалено модератором


 
_oxffff   (2013-04-24 15:20) [178]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 15:23) [179]


> Но я считаю плохим тоном создавать модуль для 3х строк кода:
>  переменной, конструктора и деструктора


Кстати, я уже писал, но повторюсь. Я считаю, что в данном случае синглтон вообще не нужен. Пусть клиенты используют библиотеку как хотят. Объявлять за них переменные с инициализированными объектами - плохая идея. Ведь никто же не заводит 2 переменные ("doubleton" :-), даже если это самое типичное использование какого-то класса.


 
DevilDevil ©   (2013-04-24 15:23) [180]

> Компромисс1 ©   (24.04.13 15:18) [176]

для того чтобы меня переубедить - достаточно предложить альтернативу лучше
"лучше" - значит эффективней в конкретной ситуации
эффективность = сумма плюсов и минусов помноженных на вектор приоритетов

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


 
_oxffff   (2013-04-24 15:24) [181]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 15:26) [182]

_oxffff   (24.04.13 15:24) [181]

Он оставил двоякое впечатление :) Я так и не понял, кто из нас с DevilDevil гопник. Наверное, оба :)

ЗЫ. Что-то я перестаю соответствовать своему нику, надо помягче быть :)


 
DevilDevil ©   (2013-04-24 15:29) [183]

Удалено модератором


 
Ega23 ©   (2013-04-24 15:31) [184]

Удалено модератором


 
_oxffff   (2013-04-24 15:32) [185]


> euru ©   (24.04.13 14:07) [163]


TStuff=class abstract
private type
 TStuffImpl=class(TStuff)

  end;

private class var Instance:TStuff;

public
class function getInstance:TStuff;static;
end;

class function getInstance:TStuff;
begin
if not assigned(Instance) then Instance:=TStuffImpl.create;
result:=Instance;
end;

Использование TStuff.getInstance()

Что касаемо создания класса TStuff помеченного как абстракт, то
не знаю как XE3 и XE4 можно ли его создавать.
Раньше было можно, но это явно баг.


 
Ega23 ©   (2013-04-24 15:34) [186]

Удалено модератором


 
Ega23 ©   (2013-04-24 15:35) [187]


> Раньше было можно, но это явно баг.


Вроде бы warning был в этом случае.


 
_oxffff   (2013-04-24 15:39) [188]


> Ega23 ©   (24.04.13 15:35) [187]
>
> > Раньше было можно, но это явно баг.
>
>
> Вроде бы warning был в этом случае.


Спасибо нужно глянуть.
Но по идее это явно ошибка.
Либо они не хотели делать динамическую проверку при создании через class of, если  трактовать как ошибку.


 
DevilDevil ©   (2013-04-24 15:40) [189]

Удалено модератором


 
DevilDevil ©   (2013-04-24 15:46) [190]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 15:47) [191]

Не знаю, рассматривалось ли такое решение.
Добавить метод класса, поставляющий этот самый синглетон.
Не функцию, а именно метод класса. Отдельный конструктор.
Возможно ли такое решение?
Допустим у меня есть TFoo.GetSingleton, возвращающий указатель на создаваемый этим методом при первом вызове глобальный и уникальный объект.
Допустим я создал класс-наследник TFoo2 = class(TFoo).
Вызов TFoo2.GetSingleton вернет указатель на объект класса TFoo2.
Или я неправ?
Если неправ, конструктор можно сделать виртуальным и перекрывать.
Но так или иначе это можно реализовать.


 
Marser ©   (2013-04-24 15:49) [192]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 15:50) [193]

DevilDevil ©   (24.04.13 15:40) [189]


> Задача - иметь 2 разных режима работы библиотеки
> Первый режим универсальный, второй режим - синглетон конкретного
> типа с конкретным именем
> Найди решение лучше, чем использовать дифайны


Я как сенсорный иррационал считаю, что тут не нужен никакой define. Если уж так хочется кроме универсального режима (который позволяет иметь "синглетон конкретного типа с конкретным именем"), иметь еще и "синглетон конкретного типа с конкретным именем", то этот "синглетон конкретного типа с конкретным именем" может быть объявлен в отдельном модуле. И если ты как интуит потребуешь доказательство, что такой подход где-то применяется, то я сообщаю, что многие библиотеки поставляются с примерами и шаблонами приложений/модулей. Часто даже в мануале к библиотеке написано - берите базовое приложение X и изменяйте его по своим нуждам


 
Аббат Пиккола   (2013-04-24 15:52) [194]

Удалено модератором


 
DevilDevil ©   (2013-04-24 15:53) [195]

> Компромисс1 ©   (24.04.13 15:50) [193]

доказательства - это для сенсориков :)
мы интуиты редко их делаем :) (без шуток, такая особенность психики)

в универсальном режиме (первый) нет синглетона
в частном режиме (второй) есть синглетон


 
Компромисс1 ©   (2013-04-24 16:02) [196]

DevilDevil ©   (24.04.13 15:53) [195]

Ты меня в конец запутал. Как я могу быть одновременно сенсориком и иррационалом? Я обожаю иррациональные доказательства? :)

Marser ©   (24.04.13 15:49) [192]

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


 
_oxffff   (2013-04-24 16:02) [197]

Если ты хочешь выбирать разные режимы сборки своего приложения в compile time, то естественно define.
Если ты хочешь выбирать разные режимы в run-time, то реализуй соответствующий подход(например один из паттернов).

Синглетон то здесь причем?


 
Аббат Пиккола   (2013-04-24 16:02) [198]

Чем-то мне это все напоминает спор о том, должен ли Бог быть един или ему можно признать сотоварищей.

Единый Бог это синглетон. Неинициализированный пока.

Таким образом Розыч предлагает именно единого неинициализированного (несуществующего без надобности) Бога. Если такая сущность понадобится, ее можно будет инициализировать функцией Foo. Перавя же молитва оживит единого Бога.

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

Раз уж спор пошел богословский, предлагаю обсудить конкретную богословскую тему в самом общем философском ключе:

МОЖЕТ ЛИ СИНГЛЕТОН ОБЛАДАТЬ ПОЛИМОРФИЗМОМ ?


 
Ega23 ©   (2013-04-24 16:04) [199]

Удалено модератором


 
_oxffff   (2013-04-24 16:05) [200]

Удалено модератором


 
Inovet ©   (2013-04-24 16:06) [201]

Удалено модератором


 
DevilDevil ©   (2013-04-24 16:07) [202]

Удалено модератором


 
Ega23 ©   (2013-04-24 16:07) [203]


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


Есть юнит с контролом, есть юнит с функцией Register. Сплошь и рядом.


 
euru ©   (2013-04-24 16:07) [204]


> _oxffff   (24.04.13 15:32) [185]

А чем реализация через абстрактный класс лучше реализации через интерфейс?

Из недостатков:
1.
var Stuff: TStuff;
. . .
Stuff := TStuff.GetInstance;
. . .
Stuff.Free;

Если этот синглтон хранит состояния, то они будут потеряны.

2. Насколько я помню, такой код не откомпилируется в D7.


 
Компромисс1 ©   (2013-04-24 16:08) [205]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 16:09) [206]

Ega23 ©   (24.04.13 16:07) [203]

Да, точно. Спасибо. Я уже и забыл, как компоненты регистрировал.


 
DevilDevil ©   (2013-04-24 16:11) [207]

> _oxffff   (24.04.13 16:02) [197]
> Если ты хочешь выбирать разные режимы сборки своего приложения
> в compile time, то естественно define.
> Если ты хочешь выбирать разные режимы в run-time, то реализуй
> соответствующий подход(например один из паттернов).
>
> Синглетон то здесь причем?


если возвращаться к [0]
то Розыч спутал причину со следствием
он счёт define своего рода реализацией синглетона
на самом деле define - это режим в рамках которого есть синглетон


 
_oxffff   (2013-04-24 16:15) [208]


> euru ©   (24.04.13 16:07) [204]
>
> > _oxffff   (24.04.13 15:32) [185]
>
> А чем реализация через абстрактный класс лучше реализации
> через интерфейс?


Собственно я не рассматривал вопрос в такой постановке. :)
Я просто поставил задачу внести реализацию в namespace класса

P.S. Что касаемо Вашего кода, то время жизни объекта тоже вызывает вопросы. ;)


 
Аббат Пиккола   (2013-04-24 16:15) [209]

Удалено модератором


 
DevilDevil ©   (2013-04-24 16:16) [210]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 16:19) [211]


> может (а следовательно, и должен)


Мне кажется, тут есть логическая ошибка.
С остальным согласен :)


 
Inovet ©   (2013-04-24 16:21) [212]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 16:21) [213]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 16:26) [214]

Удалено модератором


 
DevilDevil ©   (2013-04-24 16:31) [215]

> Аббат Пиккола   (24.04.13 16:21) [213]

а мне кстати в таком виде не нравятся синглетоны
в С++ хотябы можно скрыть автоматическую проверку на NULL, и для программиста это будет обычная переменная с обычным обращением

а вот обращение по функции мне кажется некрасиво
мне ближе объявление глобальной переменной и вызов InitializeMainFoo() например


 
Аббат Пиккола   (2013-04-24 16:41) [216]

Удалено модератором


 
DevilDevil ©   (2013-04-24 16:43) [217]

> Аббат Пиккола   (24.04.13 16:41) [216]

а глобальная функция вместо переменной кто ? )


 
Юрий Зотов ©   (2013-04-24 16:44) [218]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 16:52) [219]

Удалено модератором


 
DevilDevil ©   (2013-04-24 16:53) [220]

Удалено модератором


 
DevilDevil ©   (2013-04-24 16:56) [221]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 16:56) [222]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 16:57) [223]

Удалено модератором


 
euru ©   (2013-04-24 17:00) [224]


> _oxffff   (24.04.13 16:15) [208]
> Я просто поставил задачу внести реализацию в namespace класса

unit Unit1;

interface

type
  ISingleton = interface
  end;

type
  TSingleton = class(ISingleton)
  private
    class var Instance: TSingleton;
    constructor Create;
  public
    class function GetInstance: ISingleton;
  end;

implementation

class function TSingleton.GetInstance: ISingleton;
begin
  if TSingleton.Instance = nil then
    TSingleton.Instance = TSingleton.Create;
  Result := TSingleton.Instance;
end;

finalization
  if TSingleton.Instance <> nil then
    TSingleton.Instance.Free;
end.



> Что касаемо Вашего кода, то время жизни объекта тоже вызывает вопросы. ;)
А что не так со временем жизни объекта?


 
Компромисс1 ©   (2013-04-24 17:00) [225]

Удалено модератором


 
DevilDevil ©   (2013-04-24 17:00) [226]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 17:04) [227]

2 DevilDevil ©   (24.04.13 16:56) [221]

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

Возможно, если бы я производил библиотеки, я бы осторожнее обращался с этими вещами. Вот, например, глобальная функция Printer. Подозреваю, что это как раз и есть синглетон:

function Printer: TPrinter;
begin
 if FPrinter = nil then FPrinter := TPrinter.Create;
 Result := FPrinter;
end;

function SetPrinter(NewPrinter: TPrinter): TPrinter;
begin
 Result := FPrinter;
 FPrinter := NewPrinter;
end;


Кстати, а почему бы не решить проблему данной ветки таким вот испытанным способом?

Мне самому не нравятся не только глобальные переменные, но и даже всякие синглетоны. Но раз уж без них никак, то почему бы не сделать так, как сделано в модуле Printers ?


 
Компромисс1 ©   (2013-04-24 17:06) [228]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 17:08) [229]

В модуле Printers VCL есть синглетон Printer и функция
function SetPrinter(NewPrinter: TPrinter): TPrinter,

позволяющая заменить класс внутренней переменной FPrinter на любой TFoo2, если он наследует классу TPrinter.

Это не то, что надо?


 
Inovet ©   (2013-04-24 17:08) [230]

Удалено модератором


 
DevilDevil ©   (2013-04-24 17:11) [231]

Удалено модератором


 
DevilDevil ©   (2013-04-24 17:13) [232]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 17:15) [233]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 17:25) [234]

Удалено модератором


 
DevilDevil ©   (2013-04-24 17:25) [235]

Удалено модератором


 
Ega23 ©   (2013-04-24 17:37) [236]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 17:38) [237]

Удалено модератором


 
Inovet ©   (2013-04-24 17:38) [238]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 17:56) [239]

Удалено модератором


 
Ega23 ©   (2013-04-24 17:57) [240]

Удалено модератором


 
DevilDevil ©   (2013-04-24 18:01) [241]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 18:01) [242]

Удалено модератором


 
DevilDevil ©   (2013-04-24 18:12) [243]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 18:15) [244]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 18:25) [245]

Удалено модератором


 
Smile   (2013-04-24 18:26) [246]

Удалено модератором


 
Аббат Пиккола   (2013-04-24 18:36) [247]

Удалено модератором


 
Компромисс1 ©   (2013-04-24 18:58) [248]

Аббат Пиккола   (24.04.13 18:36) [247]

Я работал с одним framework, где почти все объекты были синглтонами, причем заменяемыми и доступными из любого контекста.
Например, был Foo.someMethod1(...), который делегировал всю работу Foo.getInstance().someMethod(...), Foo.someMethod2() делегировал всю работу Foo.getInstance().someMethod2 и т.д. Можно было писать и напрямую, так как getInstance() был public. Был и  public Foo.setInstance(foo), то есть можно было менять синглтон. Причем Foo.getInstance() всегда возвращал интерфейс, а не класс. Не могу сказать, что красиво сделано, но по крайней мере работает без проблем.


 
Ega23 ©   (2013-04-24 19:22) [249]

Есть мнение, что некоторые люди, начитавшись банды четырёх, программирование по паттернам возводят в абсолют.


 
oxffff ©   (2013-04-24 20:01) [250]


> euru ©   (24.04.13 17:00) [224]
>
> > _oxffff   (24.04.13 16:15) [208]
> > Я просто поставил задачу внести реализацию в namespace
> класса
>
> unit Unit1;
>
> interface
>
> type
>   ISingleton = interface
>   end;
>
> type
>   TSingleton = class(ISingleton)
>   private
>     class var Instance: TSingleton;
>     constructor Create;
>   public
>     class function GetInstance: ISingleton;
>   end;
>
> implementation
>
> class function TSingleton.GetInstance: ISingleton;
> begin
>   if TSingleton.Instance = nil then
>     TSingleton.Instance = TSingleton.Create;
>   Result := TSingleton.Instance;
> end;
>
> finalization
>   if TSingleton.Instance <> nil then
>     TSingleton.Instance.Free;
> end.



>
> > Что касаемо Вашего кода, то время жизни объекта тоже вызывает
> вопросы. ;)
> А что не так со временем жизни объекта?


1. Смешивание семантики объектной и ссылочной.

   class var Instance: TSingleton;
   class function GetInstance: ISingleton;

  TSingleton = class( XXX , ISingleton)

Если XXX  управляет подсчетом ссылок, то после того как
очередной крайний клиент освободит ссылку, произойдет освобождение объекта. Instance будет указывать на мусор

2. Аналогично Вашего примера(да такого нет в D7, но есть в поздних)

 (TSingleton.GetInstance as TObject).free


 
oxffff ©   (2013-04-24 20:13) [251]


> euru ©   (24.04.13 17:00) [224]


Нет, ну конечно и в моем и в Вашем 2 случае проблему можно решить.
Например так:

class destructor FinalizeClass;
begin
freeAndNil(Instance);       <-мы то знаем, что сначала nil, потом free
end;

destructor destroy;override;
begin
if assigned(Instance) then exit;
inherited;
end;


 
oxffff ©   (2013-04-24 20:17) [252]

@euru, помню ты SAP занимался.
Завтра у меня сдача на сертификат по SAP Unwired Platform на Павелецкой. :)


 
Rouse_ ©   (2013-04-24 20:18) [253]


> Чем не решение?


Почему бы и нет? :)


 
oxffff ©   (2013-04-24 20:28) [254]


> destructor destroy;override;
> begin
> if assigned(Instance) then exit;
> inherited;
> end;


Хех, поторопился. exit переходит на обязательную часть по освобождению. Тем интересней. :)


 
oxffff ©   (2013-04-24 20:33) [255]

Тогда так

class destructor FinalizeClass;
begin
freeAndNil(Instance);      
end;

procedure FreeInstance;override;
begin
if assigned(Instance) then exit;
inherited;
end;


 
oxffff ©   (2013-04-24 20:40) [256]

Точнее так, чтобы объект не произвел cleanup и не освободился где не положено

class destructor FinalizeClass;
begin
freeAndNil(Instance);      
end;

destructor destroy;override;
begin
if assigned(Instance) then exit;
inherited;
end;

procedure FreeInstance;override;
begin
if assigned(Instance) then exit;
inherited;
end;


 
Дмитрий С ©   (2013-04-25 06:05) [257]

А не проще Instance объявить тоже интерфейсом?


 
oxffff ©   (2013-04-25 08:07) [258]

Ну от  (TSingleton.GetInstance as TObject).free это объявление не спасет.


 
Дмитрий С ©   (2013-04-25 14:54) [259]

Разве интерфейсу можно сделать as Tobject?


 
euru ©   (2013-04-25 17:43) [260]


> oxffff ©   (24.04.13 20:01) [250]
> Если XXX  управляет подсчетом ссылок, то после того как очередной
> крайний клиент освободит ссылку, произойдет освобождение объекта.
> Instance будет указывать на мусор2.

Я давно уже не использую Delphi, поэтому свои примеры писал по той остаточной информации, что ещё помню. И совершенно забыл, что в Delphi у интерфейсов "странное" поведение.

В качестве решения этой проблемы можно у синглтона перегрузить методы AddRef и Release, в которых исключить подсчет ссылок (зачем их считать, если объект всегда в единственном экземпляре).


> (TSingleton.GetInstance as TObject).free

Явное приведение к TObject в данном контексте - это намеренное вредительство :)

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


 
euru ©   (2013-04-25 17:46) [261]


> oxffff ©   (24.04.13 20:17) [252]
> @euru, помню ты SAP занимался.Завтра у меня сдача на сертификат
> по SAP Unwired Platform на Павелецкой. :)

Почему занимался? Я и сейчас занимаюсь.


 
oxffff ©   (2013-04-25 18:40) [262]


> Дмитрий С ©   (25.04.13 14:54) [259]
> Разве интерфейсу можно сделать as Tobject?


Уже можно в новых версиях.


 
Дмитрий С ©   (2013-04-25 19:49) [263]


> Уже можно в новых версиях.

Прикольно. Интересно что будет, если интерфейс будет инородный.


 
Дмитрий С ©   (2013-04-25 20:35) [264]


> Уже можно в новых версиях.

Хорошо, а как насчет исключения в деструкторе?


 
han_malign   (2013-04-26 11:18) [265]

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

TXXxx = class
...
protected
   class function exclusive(): boolean; virtual; abstract;{!!!}
end;

TExclusiveXxx = class(TXXxx)
protected
   class function exclusive(): boolean; override;
end;

TSingletonXxx = class(TXXxx)
protected
   class function exclusive(): boolean; override;
public
   class function getInstance(): TSingletonXxx;
end;

- TXXxx.Create - ругнется на abstract
- TSingletonXxx.getInstance() as TExclusiveXxx - обломает на этапе компиляции...

соответственно, дабы не словить плюху при явном повышении типа(upcast) - тип TXXxx - можно использовать только при связывании внешнего контекста, в идеале - только как входной параметр функции/метода...



Страницы: 1 2 3 4 5 6 7 вся ветка

Форум: "Прочее";
Текущий архив: 2013.10.06;
Скачать: [xml.tar.bz2];

Наверх





Память: 1.23 MB
Время: 0.011 c
15-1367219106
O'ShinW
2013-04-29 11:05
2013.10.06
Нарочно не придумаешь..


2-1358414836
O'ShinW
2013-01-17 13:27
2013.10.06
Моргает ListView.(D7)


2-1358698446
Pcrepair
2013-01-20 20:14
2013.10.06
контрол имеющий пару строка-число


15-1366559274
UseSynergy
2013-04-21 19:47
2013.10.06
Ищу программистов для стартапа


2-1358236774
samborskijj
2013-01-15 11:59
2013.10.06
Неверный запрос (слишком мало параметров)





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский