"Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 24.08.23 06:40
Оценка:
Добрый день!

Есть известный шаблон IDisposable, который предназначен для освобождения ресурсов.
Он нужен по той причине, что деструктор (финализатор) не всегда корректно справляется со своей "работой".

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

Причина в том, что бывают такие ситуации, когда конструктор не всегда может выделить необходимые ресурсы.
Например, в момент создания объекта нет достаточной информации для "полноценного" создания объекта.
Поэтому такие объекты приходится создавать в следующем порядке:
1) Сначала нужно создать "пустой" объект, вызвать его конструктор. Объект создается, но пока не "захватывает" ресурсы. В таком состоянии объект не функционален.
2) Затем нужно передать этому объекту необходимую информацию (установить значения его "ключевых" свойств), от которой зависит логика "захвата" ресурсов.
3) И только теперь можно вызвать метод объекта, который выполнит захват ресурсов, назовем его InitializeResources().

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

Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?
Re: Builder
От: Qbit86 Кипр
Дата: 24.08.23 06:59
Оценка: 12 (1) +3
Здравствуйте, zelenprog, Вы писали:

Z>Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?


Паттерн Builder.
Только не тот Builder, который в книжке GoF, а просто — вынесение настройки объекта в «предварительный» объект отдельного типа.

Z>1) Сначала нужно создать "пустой" объект, вызвать его конструктор. Объект создается, но пока не "захватывает" ресурсы. В таком состоянии объект не функционален.


1) Сначала создаёшь «пустой» изменяемый билдер.

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


2) По мере того, как становится доступной информация, заполняешь билдер.

Z>3) И только теперь можно вызвать метод объекта, который выполнит захват ресурсов, назовем его InitializeResources().


3) Когда информации достаточно, то создаёшь интересующий тебя объект одним вызовом метода Build() или ToMyType(), проводя в нём инициализацию, передавая ресурсы в ктор исходного типа.

Z>В таком состоянии объект не функционален.


То есть вместо того, чтобы рассматривать состояние «не инициализирован» для объектов твоего типа, ты разделяешь их на объекты двух типов: «не функциональный» изменяемый билдер (он может только накапливать настройки) и по возможности неизменяемый рабочий объект.
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Builder
От: zelenprog  
Дата: 24.08.23 08:04
Оценка:
Q>Паттерн Builder.
Q>Только не тот Builder, который в книжке GoF, а просто — вынесение настройки объекта в «предварительный» объект отдельного типа.
Q>1) Сначала создаёшь «пустой» изменяемый билдер.
Q>2) По мере того, как становится доступной информация, заполняешь билдер.
Q>3) Когда информации достаточно, то создаёшь интересующий тебя объект одним вызовом метода Build() или ToMyType(), проводя в нём инициализацию, передавая ресурсы в ктор исходного типа.
Q>То есть вместо того, чтобы рассматривать состояние «не инициализирован» для объектов твоего типа, ты разделяешь их на объекты двух типов: «не функциональный» изменяемый билдер (он может только накапливать настройки) и по возможности неизменяемый рабочий объект.

Спасибо!
Получается, для каждого класса нужен еще один дополнительный класс Builder?
То есть количество классов увеличится вдвое.

В Википедии есть статья "Порождающие шаблоны проектирования":
https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%80%D0%BE%D0%B6%D0%B4%D0%B0%D1%8E%D1%89%D0%B8%D0%B5_%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F

Из них под ваше описание подходит "Фабричный метод":
https://ru.wikipedia.org/wiki/%D0%A4%D0%B0%D0%B1%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)

Верно я понял?
Re[3]: StringBuilder
От: Qbit86 Кипр
Дата: 24.08.23 08:19
Оценка: 9 (1) +1
Здравствуйте, zelenprog, Вы писали:

Z>Получается, для каждого класса нужен еще один дополнительный класс Builder?


Для тех классов, у которых создание/настройка «размазаны» по времени, но отделены от собственно использования. Такой подход гарантирует пользователю типа, что коль скоро у него есть созданный объект, то он уже до конца проинициализирован, и в дополнительных проверках перед использованием типа IsInitialized не нуждается.

Z>То есть количество классов увеличится вдвое.


Отдельный функционал — отдельный класс. В данном случае функционалы — постепенное конструирование объекта vs. последующее использование объекта.

Z>В Википедии есть статья "Порождающие шаблоны проектирования":

Z>Из них под ваше описание подходит "Фабричный метод":
Z>Верно я понял?

В Википедии и в GoF это крутится вокруг полиморфного создания объектов, там какие-то иерархии и ООП'шное болото.
Я же говорю про простой прямолинейный подход, think of StringBuilder vs. String.
Глаза у меня добрые, но рубашка — смирительная!
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: m2user  
Дата: 24.08.23 09:12
Оценка: +1
Z>Конечно, можно передать параметры в конструктор. Но это не всегда удобно и не всегда возможно. Например, у меня сейчас есть ограничение, чтобы конструкторы классов были вообще без параметров.

lazy initialization, либо статический метод для создания экземпляра.

P.S. ограничение на безпараметровый конструктор из-за сериализации?
Re[2]: Builder
От: qaz77  
Дата: 24.08.23 09:29
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>3) Когда информации достаточно, то создаёшь интересующий тебя объект одним вызовом метода Build() или ToMyType(), проводя в нём инициализацию, передавая ресурсы в ктор исходного типа.


Как я понял, есть ограничение, что все конструкторы без параметров.
Тогда непонятно как реализовывать порождающий метод в билдере...
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: qaz77  
Дата: 24.08.23 09:34
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?


А старая добрая двухфазная инициализация не подойдет?
В современном С++ считается скорее антипаттерном.
См. как сделаны практически все нетривиальные классы в MFC: конструктор без параметров и функции Create, CreateEx, ... с параметрами.
Примерно так:
CWnd w;
w.Create(NULL, "Hello");
Re[3]: Без параметров
От: Qbit86 Кипр
Дата: 24.08.23 09:42
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Как я понял, есть ограничение, что все конструкторы без параметров.


Это больше не является ограничением, теперь конструкторы создаваемых объектов могут иметь разное число параметров разных типов, без ограничений.
Это заменяется свойством «все методы Build()/ToMyType() без параметров». Действительно, зачем им параметры, если все необходимые данные захватываются как поля в билдерах.
Глаза у меня добрые, но рубашка — смирительная!
Re[4]: Без параметров
От: qaz77  
Дата: 24.08.23 11:46
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Это больше не является ограничением, теперь конструкторы создаваемых объектов могут иметь разное число параметров разных типов, без ограничений.

Q>Это заменяется свойством «все методы Build()/ToMyType() без параметров». Действительно, зачем им параметры, если все необходимые данные захватываются как поля в билдерах.

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

class FooBuilder
{
public:

Foo Build()
  {
     Foo f; // конструктор без параметров по условию
     // тут что?    
     return f;
  }
};
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: kov_serg Россия  
Дата: 24.08.23 12:24
Оценка:
Здравствуйте, zelenprog, Вы писали:

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


Z>Причина в том, что бывают такие ситуации, когда конструктор не всегда может выделить необходимые ресурсы.

Z>Например, в момент создания объекта нет достаточной информации для "полноценного" создания объекта.
А нафига вам вообще конструктор. Получайте сразу готовый к работе класс, запрашивая его у посредника:
  something=di.GetSomething()

Z>Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?
Смотри dependency inversion и factory. Они уже внутри если надо создают подходящий класс и настраивают его для работы.
Re[5]: Без параметров
От: Qbit86 Кипр
Дата: 24.08.23 13:23
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Понятно, что есть поля в билдере.

Q>Как потом чисто технически эти значения перекочуют в целевой объект?
Q>Т.е. билдер должен быть френдом целевого типа?

Судя по формулировке, у топик-стартера Дотнет. Там вместо френдов — или обращение к internal членам при необходимости, или сам билдер делается вложенным (nested) классом. Пример из стандартной библиотеки — ImmutableDictionary<TKey,TValue>.Builder.

Q>Просто присвоить значения своих полей в закрытое состояние создаваемого объекта?


Скопировать или даже переместить (move) в конструктор целевого объекта. Конструктор целевого объекта желательно делать «тупым», чтоб он просто захватывал передаваемые параметры. Соответственно, после move состояние билдера может быть «очищенным».

Q>конструктор без параметров по условию


Если топик-стартер придумал себе такое условие «конструктор без параметров», то он с тем же успехом может заменить его условием «фабричный метод без параметров», или «конструктор билдера/фабрики без параметров» (в зависимости от того, где у него это ограничение всплывает).
Глаза у меня добрые, но рубашка — смирительная!
Re[6]: Без параметров
От: qaz77  
Дата: 24.08.23 13:27
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Судя по формулировке, у топик-стартера Дотнет.


Ну вот для меня это нифига не очевидно.
Ждемс уточнений и комментариев от ТС.
Re[7]: IDisposable наоборот
От: Qbit86 Кипр
Дата: 24.08.23 13:32
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>>Судя по формулировке, у топик-стартера Дотнет.


Q>Ну вот для меня это нифига не очевидно.


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


IDisposable, финализатор и корректность его работы (вероятно, имеется в виду недетерминированность) — это Дотнет-специфичное.
Глаза у меня добрые, но рубашка — смирительная!
Re[8]: IDisposable наоборот
От: qaz77  
Дата: 24.08.23 13:36
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>IDisposable, финализатор и корректность его работы (вероятно, имеется в виду недетерминированность) — это Дотнет-специфичное.


А что, в дотнете запрещены конструкторы с параметрами? Откуда такие ограничения? Или я что-то пропустил?
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: Ziaw Россия  
Дата: 24.08.23 13:44
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>1) Сначала нужно создать "пустой" объект, вызвать его конструктор. Объект создается, но пока не "захватывает" ресурсы. В таком состоянии объект не функционален.

Z>2) Затем нужно передать этому объекту необходимую информацию (установить значения его "ключевых" свойств), от которой зависит логика "захвата" ресурсов.
Z>3) И только теперь можно вызвать метод объекта, который выполнит захват ресурсов, назовем его InitializeResources().

Какую функцию выполняет пустой нефункциональный объект между шагами 1 и 2? Если собирает данные конфигурации, то SRPшнее выделить их в отдельный объект, который потом передадим в конструктор/фабричный метод, который и выполнит захват ресурсов.

using (new AResource(resourceConfig)) 
{
  // ...
}

using (resourceConfig.CreateAResource()) 
{
  // ...
}
Re[9]: new constraint
От: Qbit86 Кипр
Дата: 24.08.23 13:57
Оценка: +1
Здравствуйте, qaz77, Вы писали:

Q>А что, в дотнете запрещены конструкторы с параметрами? Откуда такие ограничения? Или я что-то пропустил?


The new constraint specifies that a type argument in a generic class or method declaration must have a public parameterless constructor.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/new-constraint


Но это не то чтобы было прям очень полезно; в обобщённом коде здорового человека от таких ограничений можно/желательно отказаться.
Глаза у меня добрые, но рубашка — смирительная!
Re: "Гибкий" конструктор для инициализации объекта класса (а
От: Sm0ke Россия ksi
Дата: 25.08.23 00:26
Оценка:
Здравствуйте, zelenprog

Желательно разложить всё по полочкам

Как понимаю три пункта работы приложения:
* Инициализация
* Работа
* Завершение



Задача первого пункта

Подготовить к работе все ресурсы.

Вот от них кажись и надо плясать. Какие виды ресурсов для работы нужны? (model_3d, music_background, etc)



Для каждого Делаем класс: ресурс такой-то

Для формирования этого ресурса есть параметры

можно сложить эти параметры в структуру (обычно без методов): params_init_ast (или что там)
* Каждому набору параметров формирования своя структура.

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




Создание объекта с параметрами формирования в некоторых случаях удобно поручить отдельному классу.



Чтож, вычислили параметры, закинули в конструктор / или там в метод init()
Что дальше?
Если вдруг окажется, что класс ресурса нуждается в общении с другими классами (назовём "вычислители"), то есть от них зависит
То почему бы не добавить этих "вычислителей" как поля для структуры params_
Да капитан, отдельный параметр ясен перец может быть классом.

struct params_car_holder (text name, float size, agent orange)

// метод-аксесор
params_car_holder::get_age() { return this.orange.calc(this.size) }




Что по файлам? Хорошо ли отделить по разным классам: А логику загрузки данных от Б набор ресурсов для хранения и обработки?
Когда загрузчики более не нужны — некоторые можно выгрузить из оперативки. Хотя во втором пункте бывает нужно что-то подгружать)



Система
  Ресурсы
  Обработчики


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


Что передавать в метод обработчика? Конкретный ресурс, Всю систему, или Отдельную её часть?
Отредактировано 25.08.2023 2:00 Sm0ke . Предыдущая версия . Еще …
Отредактировано 25.08.2023 2:00 Sm0ke . Предыдущая версия .
Отредактировано 25.08.2023 0:57 Sm0ke . Предыдущая версия .
Отредактировано 25.08.2023 0:53 Sm0ke . Предыдущая версия .
Отредактировано 25.08.2023 0:51 Sm0ke . Предыдущая версия .
Отредактировано 25.08.2023 0:50 Sm0ke . Предыдущая версия .
Отредактировано 25.08.2023 0:47 Sm0ke . Предыдущая версия .
Отредактировано 25.08.2023 0:34 Sm0ke . Предыдущая версия .
Отредактировано 25.08.2023 0:33 Sm0ke . Предыдущая версия .
Отредактировано 25.08.2023 0:31 Sm0ke . Предыдущая версия .
Re[3]: Builder
От: Sm0ke Россия ksi
Дата: 25.08.23 02:03
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>Получается, для каждого класса нужен еще один дополнительный класс Builder?

Z>То есть количество классов увеличится вдвое.

Не обязательно для каждого
Re[3]: Builder
От: zelenprog  
Дата: 25.08.23 07:49
Оценка:
Отвечаю на все вопросы в этой ветке.

Q>Как я понял, есть ограничение, что все конструкторы без параметров.


Да, именно так. Это ограничение скриптового языка, которым я пользуюсь.
В нем реализовано урезанное ООП.

Q>Тогда непонятно как реализовывать порождающий метод в билдере...


Кстати, да, как это сделать?

Q>Судя по формулировке, у топик-стартера Дотнет. Там вместо френдов — или обращение к internal членам при необходимости, или сам билдер делается вложенным (nested) классом. Пример из стандартной библиотеки — ImmutableDictionary<TKey,TValue>.Builder.


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

Q>Если топик-стартер придумал себе такое условие «конструктор без параметров», то он с тем же успехом может заменить его условием «фабричный метод без параметров», или «конструктор билдера/фабрики без параметров» (в зависимости от того, где у него это ограничение всплывает).


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

Q>

Есть известный шаблон IDisposable, который предназначен для освобождения ресурсов.
Q>Он нужен по той причине, что деструктор (финализатор) не всегда корректно справляется со своей "работой".
Q>...аналогичный по схеме IDisposable, но противоположный по значению.

Q>IDisposable, финализатор и корректность его работы (вероятно, имеется в виду недетерминированность) — это Дотнет-специфичное.

В этом скриптовом языке работа с памятью очень похожа на Дотнет.
За освобождением следит некий сборщик мусора. Когда ссылка на объект становится "свободной" — память освобождается.
Но назначение Dispose — освобождать ресурсы тогда, когда нужно разработчику, не дожидаясь сборщика мусора.
По моему такой подход сейчас есть во многих современных языках. Ну и я его применяю на "моем" скриптовом языке.

Так что актуальным остается вопрос: как формировать в билдере объект, если у этого объекта есть только один конструктор без параметров?

По сути мне надо сделать примерно следующее:
class Test
{
   string mFileName;
   File mFile;

   void Set_FileName (pFileName)
   {
      mFileName = pFileName;
   }

   void Init ()
   {
      mFile = FileOpen(mFileName);
   }
};

lTest = new Test;
lTest.Set_FileName("c:\test_file.txt")
lTest.Init();
Отредактировано 25.08.2023 8:07 zelenprog . Предыдущая версия .
Re[2]: "Гибкий" конструктор для инициализации объекта класса
От: zelenprog  
Дата: 25.08.23 07:52
Оценка:
M> ... статический метод для создания экземпляра.

Статических методов нету в "моем" скриптовом языке.

M>P.S. ограничение на безпараметровый конструктор из-за сериализации?


Не из-за сериализации.
В этом скриптовом языке "урезанное" ООП. Все классы могут иметь только один конструктор без параметров.

M>lazy initialization


lazy initialization скорее всего подходит.
Но ведь это же плохой шаблон?

По сути мне надо сделать сто-то типа этого:

class Test
{
   string mFileName;
   File mFile;

   void Set_FileName (pFileName)
   {
      mFileName = pFileName;
   }

   void Init ()
   {
      mFile = FileOpen(mFileName);
   }
};

lTest = new Test;
lTest.Set_FileName("c:\test_file.txt")
lTest.Init();
Отредактировано 25.08.2023 8:06 zelenprog . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.