Есть известный шаблон IDisposable, который предназначен для освобождения ресурсов.
Он нужен по той причине, что деструктор (финализатор) не всегда корректно справляется со своей "работой".
У меня возник похожий вопрос, только не для освобождения, а для выделения ресурсов.
Причина в том, что бывают такие ситуации, когда конструктор не всегда может выделить необходимые ресурсы.
Например, в момент создания объекта нет достаточной информации для "полноценного" создания объекта.
Поэтому такие объекты приходится создавать в следующем порядке:
1) Сначала нужно создать "пустой" объект, вызвать его конструктор. Объект создается, но пока не "захватывает" ресурсы. В таком состоянии объект не функционален.
2) Затем нужно передать этому объекту необходимую информацию (установить значения его "ключевых" свойств), от которой зависит логика "захвата" ресурсов.
3) И только теперь можно вызвать метод объекта, который выполнит захват ресурсов, назовем его InitializeResources().
Конечно, можно передать параметры в конструктор. Но это не всегда удобно и не всегда возможно. Например, у меня сейчас есть ограничение, чтобы конструкторы классов были вообще без параметров.
Получается, нужен какой-то "шаблон" для выделения ресурсов, аналогичный по схеме IDisposable, но противоположный по значению.
Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?
Здравствуйте, zelenprog, Вы писали:
Z>Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?
Паттерн Builder.
Только не тот Builder, который в книжке GoF, а просто — вынесение настройки объекта в «предварительный» объект отдельного типа.
Z>1) Сначала нужно создать "пустой" объект, вызвать его конструктор. Объект создается, но пока не "захватывает" ресурсы. В таком состоянии объект не функционален.
1) Сначала создаёшь «пустой» изменяемый билдер.
Z>2) Затем нужно передать этому объекту необходимую информацию (установить значения его "ключевых" свойств), от которой зависит логика "захвата" ресурсов.
2) По мере того, как становится доступной информация, заполняешь билдер.
Z>3) И только теперь можно вызвать метод объекта, который выполнит захват ресурсов, назовем его InitializeResources().
3) Когда информации достаточно, то создаёшь интересующий тебя объект одним вызовом метода Build() или ToMyType(), проводя в нём инициализацию, передавая ресурсы в ктор исходного типа.
Z>В таком состоянии объект не функционален.
То есть вместо того, чтобы рассматривать состояние «не инициализирован» для объектов твоего типа, ты разделяешь их на объекты двух типов: «не функциональный» изменяемый билдер (он может только накапливать настройки) и по возможности неизменяемый рабочий объект.
Q>Паттерн Builder. Q>Только не тот Builder, который в книжке GoF, а просто — вынесение настройки объекта в «предварительный» объект отдельного типа. Q>1) Сначала создаёшь «пустой» изменяемый билдер. Q>2) По мере того, как становится доступной информация, заполняешь билдер. Q>3) Когда информации достаточно, то создаёшь интересующий тебя объект одним вызовом метода Build() или ToMyType(), проводя в нём инициализацию, передавая ресурсы в ктор исходного типа. Q>То есть вместо того, чтобы рассматривать состояние «не инициализирован» для объектов твоего типа, ты разделяешь их на объекты двух типов: «не функциональный» изменяемый билдер (он может только накапливать настройки) и по возможности неизменяемый рабочий объект.
Спасибо!
Получается, для каждого класса нужен еще один дополнительный класс Builder?
То есть количество классов увеличится вдвое.
Здравствуйте, zelenprog, Вы писали:
Z>Получается, для каждого класса нужен еще один дополнительный класс Builder?
Для тех классов, у которых создание/настройка «размазаны» по времени, но отделены от собственно использования. Такой подход гарантирует пользователю типа, что коль скоро у него есть созданный объект, то он уже до конца проинициализирован, и в дополнительных проверках перед использованием типа IsInitialized не нуждается.
Z>То есть количество классов увеличится вдвое.
Отдельный функционал — отдельный класс. В данном случае функционалы — постепенное конструирование объекта vs. последующее использование объекта.
Z>В Википедии есть статья "Порождающие шаблоны проектирования": Z>Из них под ваше описание подходит "Фабричный метод": Z>Верно я понял?
В Википедии и в GoF это крутится вокруг полиморфного создания объектов, там какие-то иерархии и ООП'шное болото.
Я же говорю про простой прямолинейный подход, think of StringBuilder vs. String.
Глаза у меня добрые, но рубашка — смирительная!
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
Z>Конечно, можно передать параметры в конструктор. Но это не всегда удобно и не всегда возможно. Например, у меня сейчас есть ограничение, чтобы конструкторы классов были вообще без параметров.
lazy initialization, либо статический метод для создания экземпляра.
P.S. ограничение на безпараметровый конструктор из-за сериализации?
Здравствуйте, Qbit86, Вы писали:
Q>3) Когда информации достаточно, то создаёшь интересующий тебя объект одним вызовом метода Build() или ToMyType(), проводя в нём инициализацию, передавая ресурсы в ктор исходного типа.
Как я понял, есть ограничение, что все конструкторы без параметров.
Тогда непонятно как реализовывать порождающий метод в билдере...
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
Здравствуйте, zelenprog, Вы писали:
Z>Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?
А старая добрая двухфазная инициализация не подойдет?
В современном С++ считается скорее антипаттерном.
См. как сделаны практически все нетривиальные классы в MFC: конструктор без параметров и функции Create, CreateEx, ... с параметрами.
Примерно так:
Здравствуйте, qaz77, Вы писали:
Q>Как я понял, есть ограничение, что все конструкторы без параметров.
Это больше не является ограничением, теперь конструкторы создаваемых объектов могут иметь разное число параметров разных типов, без ограничений.
Это заменяется свойством «все методы Build()/ToMyType() без параметров». Действительно, зачем им параметры, если все необходимые данные захватываются как поля в билдерах.
Здравствуйте, Qbit86, Вы писали:
Q>Это больше не является ограничением, теперь конструкторы создаваемых объектов могут иметь разное число параметров разных типов, без ограничений. Q>Это заменяется свойством «все методы Build()/ToMyType() без параметров». Действительно, зачем им параметры, если все необходимые данные захватываются как поля в билдерах.
Понятно, что есть поля в билдере.
Как потом чисто технически эти значения перекочуют в целевой объект?
Т.е. билдер должен быть френдом целевого типа?
Просто присвоить значения своих полей в закрытое состояние создаваемого объекта?
class FooBuilder
{
public:
Foo Build()
{
Foo f; // конструктор без параметров по условию
// тут что? return f;
}
};
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
Здравствуйте, zelenprog, Вы писали:
Z>У меня возник похожий вопрос, только не для освобождения, а для выделения ресурсов.
Z>Причина в том, что бывают такие ситуации, когда конструктор не всегда может выделить необходимые ресурсы. Z>Например, в момент создания объекта нет достаточной информации для "полноценного" создания объекта.
А нафига вам вообще конструктор. Получайте сразу готовый к работе класс, запрашивая его у посредника:
something=di.GetSomething()
Z>Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?
Смотри dependency inversion и factory. Они уже внутри если надо создают подходящий класс и настраивают его для работы.
Здравствуйте, qaz77, Вы писали:
Q>Понятно, что есть поля в билдере. Q>Как потом чисто технически эти значения перекочуют в целевой объект? Q>Т.е. билдер должен быть френдом целевого типа?
Судя по формулировке, у топик-стартера Дотнет. Там вместо френдов — или обращение к internal членам при необходимости, или сам билдер делается вложенным (nested) классом. Пример из стандартной библиотеки — ImmutableDictionary<TKey,TValue>.Builder.
Q>Просто присвоить значения своих полей в закрытое состояние создаваемого объекта?
Скопировать или даже переместить (move) в конструктор целевого объекта. Конструктор целевого объекта желательно делать «тупым», чтоб он просто захватывал передаваемые параметры. Соответственно, после move состояние билдера может быть «очищенным».
Q>конструктор без параметров по условию
Если топик-стартер придумал себе такое условие «конструктор без параметров», то он с тем же успехом может заменить его условием «фабричный метод без параметров», или «конструктор билдера/фабрики без параметров» (в зависимости от того, где у него это ограничение всплывает).
Здравствуйте, qaz77, Вы писали:
Q>>Судя по формулировке, у топик-стартера Дотнет.
Q>Ну вот для меня это нифига не очевидно.
Есть известный шаблон IDisposable, который предназначен для освобождения ресурсов.
Он нужен по той причине, что деструктор (финализатор) не всегда корректно справляется со своей "работой".
...аналогичный по схеме IDisposable, но противоположный по значению.
IDisposable, финализатор и корректность его работы (вероятно, имеется в виду недетерминированность) — это Дотнет-специфичное.
Здравствуйте, Qbit86, Вы писали:
Q>IDisposable, финализатор и корректность его работы (вероятно, имеется в виду недетерминированность) — это Дотнет-специфичное.
А что, в дотнете запрещены конструкторы с параметрами? Откуда такие ограничения? Или я что-то пропустил?
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
Здравствуйте, zelenprog, Вы писали:
Z>1) Сначала нужно создать "пустой" объект, вызвать его конструктор. Объект создается, но пока не "захватывает" ресурсы. В таком состоянии объект не функционален. Z>2) Затем нужно передать этому объекту необходимую информацию (установить значения его "ключевых" свойств), от которой зависит логика "захвата" ресурсов. Z>3) И только теперь можно вызвать метод объекта, который выполнит захват ресурсов, назовем его InitializeResources().
Какую функцию выполняет пустой нефункциональный объект между шагами 1 и 2? Если собирает данные конфигурации, то SRPшнее выделить их в отдельный объект, который потом передадим в конструктор/фабричный метод, который и выполнит захват ресурсов.
using (new AResource(resourceConfig))
{
// ...
}
using (resourceConfig.CreateAResource())
{
// ...
}
Как понимаю три пункта работы приложения:
* Инициализация
* Работа
* Завершение
—
Задача первого пункта
Подготовить к работе все ресурсы.
Вот от них кажись и надо плясать. Какие виды ресурсов для работы нужны? (model_3d, music_background, etc)
—
Для каждого Делаем класс: ресурс такой-то
Для формирования этого ресурса есть параметры
можно сложить эти параметры в структуру (обычно без методов): params_init_ast (или что там)
* Каждому набору параметров формирования своя структура.
тут думаю лучше обойтись без наследования, даже если разные params_ имеют одинаковые поля
чтобы было легче дорабатывать. Плюс за наглядность, чтобы не искать в исходниках базу
—
Создание объекта с параметрами формирования в некоторых случаях удобно поручить отдельному классу.
—
Чтож, вычислили параметры, закинули в конструктор / или там в метод init()
Что дальше?
Если вдруг окажется, что класс ресурса нуждается в общении с другими классами (назовём "вычислители"), то есть от них зависит
То почему бы не добавить этих "вычислителей" как поля для структуры params_
Да капитан, отдельный параметр ясен перец может быть классом.
Что по файлам? Хорошо ли отделить по разным классам: А логику загрузки данных от Бнабор ресурсов для хранения и обработки?
Когда загрузчики более не нужны — некоторые можно выгрузить из оперативки. Хотя во втором пункте бывает нужно что-то подгружать)
—
Система
Ресурсы
Обработчики
В таком раскладе обработчиков можно динамически подменять налету. Типо когда в игре по сети теряется соединение и происходит переход в автономный режим.
(за союзника начинает продолжать управление временно автопилот)
Сорян, пример надуманный, я ещё не работал в гейм деве.
Что передавать в метод обработчика? Конкретный ресурс, Всю систему, или Отдельную её часть?
Здравствуйте, zelenprog, Вы писали:
Z>Получается, для каждого класса нужен еще один дополнительный класс Builder? Z>То есть количество классов увеличится вдвое.
Отвечаю на все вопросы в этой ветке.
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();