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[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[3]: Builder
От: sergii.p  
Дата: 25.08.23 12:46
Оценка: :))
Здравствуйте, zelenprog, Вы писали:

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


можно и без дополнительного класса

class Test
{
   string mFileName;
   File mFile;

   Test Set_FileName(pFileName)
   {
      Test res = *this;
      res.mFileName = pFileName;
      return res;
   }

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

int main() {
    Test t = Test().Set_FileName("foo.txt").Init();
}
Re: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: m2user  
Дата: 24.08.23 09:12
Оценка: +1
Z>Конечно, можно передать параметры в конструктор. Но это не всегда удобно и не всегда возможно. Например, у меня сейчас есть ограничение, чтобы конструкторы классов были вообще без параметров.

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

P.S. ограничение на безпараметровый конструктор из-за сериализации?
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[3]: "Гибкий" конструктор для инициализации объекта класса
От: m2user  
Дата: 26.08.23 06:57
Оценка: +1
Z>Статических методов нету в "моем" скриптовом языке.
Z>В этом скриптовом языке "урезанное" ООП. Все классы могут иметь только один конструктор без параметров.

ясно.

Z>lazy initialization скорее всего подходит.

Z>Но ведь это же плохой шаблон?

Почему?
Он просто иногда сложнее реализуем, например когда класс должен быть thread-safe.

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


Что будет если вызвать Set_FileName после вызова Init?
По-видимому ObjectInitException (по аналогии с ObjectDisposedException)...

Выглядит не очень.
Подход с классами builder`ами выглядит более прозрачным и понятным.
Классов будет больше, но при этом методов в сумме примерно столько же.
Re[4]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.09.23 11:16
Оценка: +1
Здравствуйте, zelenprog, Вы писали:

Z>Ведь для уничтожения объектов разработали общепринятый IDisposable.

Z>Значит, еще нужен и IInitialaizable.

Положа руку на сердце, это выглядит как решение искусственно созданной проблемы.
Вот в дотнете конструктор уже является "методом Init", просто языки вроде C# более-менее следят за тем, чтобы не дать программисту воспользоваться объектом в недоинициализированом состоянии.
С точки зрения CLR, к моменту вызова конструктора объект уже полностью сконструирован — у него корректная VMT, и в полях нет никакого булшита (который мог бы помешать работе GC — например, случайные ссылки).
То есть авторы шарпа постарались максимально скрыть от пользователя этот зазор между "конструктором" и "Init". А вы зачем-то выпячиваете его наружу.
Фактически, тщательно переносите в рантайм проверку, которую прекрасно мог взять на себя компилятор.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 26.09.23 05:41
Оценка: +1
Здравствуйте, Разраб, Вы писали:

Z>>Это не искусственная проблема.

Z>>Это особенность "моей" платформы.
Р>1С?

Да. Не хотелось этого афишировать.
Боюсь, что упоминание про 1С уведет обсуждение в сторону.
А мне хотелось бы и на 1С написать программу с "правильным" подходом и "правильной" архитектурой. Нужно только придумать как грамотно преодолеть некоторые ограничения платформы.
"Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 24.08.23 06:40
Оценка:
Добрый день!

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

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

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

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

Есть ли какие-либо шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса?
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[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: "Гибкий" конструктор для инициализации объекта класса (а
От: 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 . Предыдущая версия .
Re[2]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 25.08.23 08:10
Оценка:
Q>А старая добрая двухфазная инициализация не подойдет?

Q>См. как сделаны практически все нетривиальные классы в MFC: конструктор без параметров и функции Create, CreateEx, ... с параметрами.

Q>Примерно так:
Q>
Q>CWnd w;
Q>w.Create(NULL, "Hello");
Q>


Подойдет.

Q>В современном С++ считается скорее антипаттерном.


Ну вот это и смущает, что это антипаттерн.
Поэтому я и задал здесь вопрос: можно ли как-то добиться такого же результата только с помощью какого-либо "правильного" паттерна?
Re[2]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 25.08.23 08:13
Оценка:
_>А нафига вам вообще конструктор. Получайте сразу готовый к работе класс, запрашивая его у посредника:
_>
_>  something=di.GetSomething()
_>


_>Смотри dependency inversion и factory. Они уже внутри если надо создают подходящий класс и настраивают его для работы.


А как посредник\factory будет создавать и настраивать "целевой" класс, если у этого "целевого" класса нету конструктора с параметрами?
Похожий ответ\вопрос вот в этой ветке прозвучал:
https://rsdn.org/forum/design/8586953.1
Автор: qaz77
Дата: 24.08.23
Re[2]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 25.08.23 08:28
Оценка:
Z>Какую функцию выполняет пустой нефункциональный объект между шагами 1 и 2?

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

Z>Если собирает данные конфигурации...


Этот объект не собирает данные. Он ждет пока эти данные будут ему переданы извне.
Так как не хотелось бы, чтобы этот класс знал больше, чем ему нужно.

Z>... то SRPшнее выделить их в отдельный объект, который потом передадим в конструктор/фабричный метод, который и выполнит захват ресурсов.


Да, так правильнее.
Но у конструктора нету параметров
Re[2]: "Гибкий" конструктор для инициализации объекта класса (а
От: zelenprog  
Дата: 25.08.23 08:32
Оценка:
S>Чтож, вычислили параметры, закинули в конструктор / или там в метод init()

А что за метод init()?

Предлагаете делать у каждого класса такой метод?
Напомню: у конструктора нету параметров.
Re[3]: "Гибкий" конструктор для инициализации объекта класса
От: Sm0ke Россия ksi
Дата: 25.08.23 13:04
Оценка:
Здравствуйте, zelenprog, Вы писали:


S>>Чтож, вычислили параметры, закинули в конструктор / или там в метод init()


Z>А что за метод init()?


Z>Предлагаете делать у каждого класса такой метод?

Z>Напомню: у конструктора нету параметров.

Каждому классу, требующему инициализацию, исходя из параметров
можно сделал метод init(params), раз конструктор без параметров.



Или: Про фабрику вам уже советовали

это Можно сделать так:

class Car ( int wheels, float max_speed )

class Test ( string path, file handle )

// фабрика
class maker ()

maker::new_car(int wheels, float max_speed)
{
  ret = new Car;
  ret.wheels = wheels;
  ret.max_speed = max_speed;
  return ret;
}

maker::new_test(string path)
{
  ret = new Test;
  ret.path = path;
  ret.handle = CreateFile(path);
  return ret;
}


Что тут происходит? Это класс maker, в котором несколько методов для создания целевых объектов. Фабричный метод создаёт, инициализирует и возвращает готовый экземпляр требуемого типа.

Не все языки позволяют выносить методы отдельно за пределы class def
// фабрика
class maker (

  Car new_car(int wheels, float max_speed)
  {
    ret = new Car;
    ret.wheels = wheels;
    ret.max_speed = max_speed;
    return ret;
  }

  Test new_test(string path)
  {
    ret = new Test;
    ret.path = path;
    ret.handle = CreateFile(path);
    return ret;
  }
)




кст. В вашем языке можно передать и хранить тип данных как значение?
Отредактировано 25.08.2023 13:21 Sm0ke . Предыдущая версия . Еще …
Отредактировано 25.08.2023 13:17 Sm0ke . Предыдущая версия .
Re[4]: "Гибкий" конструктор для инициализации объекта класса
От: zelenprog  
Дата: 25.08.23 14:13
Оценка:
S>кст. В вашем языке можно передать и хранить тип данных как значение?

Можно тип данных хранить в виде строки. Например:

lStr_ClassName = "T_Test";
lObj = CreateObject(lStr_ClassName);
Re[2]: "Гибкий" конструктор для инициализации объекта класса (а
От: Sm0ke Россия ksi
Дата: 26.08.23 15:43
Оценка:
Здравствуйте, Sm0ke, Вы писали:

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

Не обязательно кстати на каждый класс заводить отдельный класс фабрики. Одна фабрика может иметь несколько разных фабричных методов, для целевых классов — объединённых по смыслу.
Re[4]: "Гибкий" конструктор для инициализации объекта класса
От: zelenprog  
Дата: 29.08.23 05:34
Оценка:
M>Что будет если вызвать Set_FileName после вызова Init?
M>По-видимому ObjectInitException (по аналогии с ObjectDisposedException)...
M>Выглядит не очень.

Так ведь если применить шаблон lazy initialization, то получится то же самое.
Разве нет?
Re[5]: "Гибкий" конструктор для инициализации объекта класса
От: m2user  
Дата: 29.08.23 07:11
Оценка:
Z>Так ведь если применить шаблон lazy initialization, то получится то же самое.
Z>Разве нет?

Отложенная инициализация это не про передачу параметров, а про то, когда и при каких условиях происходит "дорогая" (ресурсоемкая) операция.
Из стартового сообщения у меня сложилось впечатление, что задача состоит в том, чтобы ресурсоемкоую операцию (а не передачу параметров) убрать из конструктора.
После Ваших пояснений про беспараметровый конструктор и статические методы, я полагаю, что lazy initialization в приведенном Вами примере (class Test) можно применить разве что для того, чтобы убрать метод Init (или сделать его вызов опциональным).

К своему предыдущему комментарию про builder добавлю, что методы все-таки будут дублироваться, при этом я бы возвращал из билдера объект базового класса (или интерфейса) без Set/Init-методов.
Re[3]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: Ziaw Россия  
Дата: 29.08.23 13:40
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>Этот объект не собирает данные. Он ждет пока эти данные будут ему переданы извне.

Z>Так как не хотелось бы, чтобы этот класс знал больше, чем ему нужно.

Не увидел противоречия. Активно он собирает или пассивно, его функция в это время — сбор данных в одну сущность для последующего применения.

Z>Да, так правильнее.

Z>Но у конструктора нету параметров

Если у конструктора не бывает параметров, то какой же это конструктор в общем случае?

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

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

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

Думаю лучше обсуждать на примере.
Вот упрощенный пример описываемой проблемы.

Простая программа читает список товаров и отображает их на форме.
Программа умеет работать с БД двух типов, у которых немного отличается структура и наименования полей. Соответственно для каждого типа БД нужен свой Адаптер.
Класс DBGoodsReader ("Читатель") не зависит от конкретной БД, он реализует общий алгоритм чтения товаров: сначала с помощью Адаптера извлекает данные, затем выполняет их некоторые преобразования ("подготовку" для отображения).

Получается, алгоритм такой:
1) Определяем тип БД
2) Создаем Адаптер, соответствующий типу БД
3) Передаем этот Адаптер классу "Читатель" (DBGoodsReader)
4) Читаем и отображаем данные.

Проблема в том, что конструкторы не

Class MainForm
{
    string mFileName;
    
    void OnButton_ReadDatabase()
    {
        T_DatabaseInfo lDatabaseInfo = new T_DatabaseInfo(mFileName);
        T_DBGoodsReader lDBGoodsReader = new T_DBGoodsReader;
        I_DBAdapter lDBAdapter = null;
        
        if lDatabaseInfo.DBType = Database.DBType1 then
            lDBAdapter = new T_DBAdapter1(mFileName);
        elseif lDatabaseInfo.DBType = Database.DBType2
            lDBAdapter = new T_DBAdapter2(mFileName);
        else
            // исключение "неизвестный тип БД
        endif
        
        lDBGoodsReader.SetAdapter(lAdapter);
        lDBGoodsReader.Init();
        lDBGoodsReader.Read();
        
        // показать на форме список товаров
    }
}


Напомню, что в конструктор невозможно передать параметры.
Приходится для "Читателя" cделать метод "SetAdapter", чтобы передать ему конкретный объект "Адаптер". И затем вызвать метод "Init".
Я читал, что это анти-паттерн. Но как сделать по другому?

Второй вопрос. Кто (какой объект) должен отвечать за проверку типа базы и создание конкретного Адаптера?
Сейчас это делается в Форме. Но по "теории" форма не должна ничего знать о таких "подробностях". Она должна просто использовать "Читателя".
Re[6]: "Гибкий" конструктор для инициализации объекта класса
От: zelenprog  
Дата: 31.08.23 05:54
Оценка:
M>Отложенная инициализация это не про передачу параметров, а про то, когда и при каких условиях происходит "дорогая" (ресурсоемкая) операция.

ОК.

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


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

M>После Ваших пояснений про беспараметровый конструктор и статические методы, я полагаю, что lazy initialization в приведенном Вами примере (class Test) можно применить разве что для того, чтобы убрать метод Init (или сделать его вызов опциональным).

M>К своему предыдущему комментарию про builder добавлю, что методы все-таки будут дублироваться, при этом я бы возвращал из билдера объект базового класса (или интерфейса) без Set/Init-методов.

Прошу перейти к обсуждению этой проблемы на примере.
Я описал пример вот в этом сообщении:
https://rsdn.org/forum/design/8590957.1
Автор: zelenprog
Дата: 31.08.23
Re[4]: Builder
От: Sinclair Россия https://github.com/evilguest/
Дата: 05.09.23 07:42
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>можно и без дополнительного класса

Можно, но нельзя:

class Test
{
   string mFileName;
   File mFile;

   Test Set_FileName(pFileName)
   {
      Test res = *this;
      res.mFileName = pFileName;
      return res;
   }

   void Init ()
   {
      mFile = FileOpen(mFileName);
   }
   Test Init_Corrected ()
   {
      Test res = *this;
      res.mFile = FileOpen(mFileName);
      return res;
   }
   int Read(byte* dest, int count)
   {
      return FileRead(mFile, dest, count);
   }
};

int main() {
   Test t1 = Test().Set_FileName("foo.txt").Init(); // compile error: cannot convert expression of type 'void' to type 'Test'
   Test t2 = Test();
   Test t3 = Test().Set_FileName("foo.txt");
   Test t4 = Test().Set_FileName("foo.txt").Init_Corrected(); 
   byte* buffer = new byte[1024];
   t2.Read(buffer, 1024); // oops
   t3.Read(buffer, 1024); // oops
   t4.Read(buffer, 1024); // :)
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: kov_serg Россия  
Дата: 05.09.23 10:50
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>Думаю лучше обсуждать на примере.

Z>Вот упрощенный пример описываемой проблемы.

Z>Простая программа читает список товаров и отображает их на форме.

Z>Программа умеет работать с БД двух типов, у которых немного отличается структура и наименования полей. Соответственно для каждого типа БД нужен свой Адаптер.
Z>Класс DBGoodsReader ("Читатель") не зависит от конкретной БД, он реализует общий алгоритм чтения товаров: сначала с помощью Адаптера извлекает данные, затем выполняет их некоторые преобразования ("подготовку" для отображения).

Z>Получается, алгоритм такой:

Z>1) Определяем тип БД
Z>2) Создаем Адаптер, соответствующий типу БД
Z>3) Передаем этот Адаптер классу "Читатель" (DBGoodsReader)
Z>4) Читаем и отображаем данные.

Z>Проблема в том, что конструкторы не


Z>
Z>Class MainForm
Z>{
Z>    string mFileName;
    
Z>    void OnButton_ReadDatabase()
Z>    {
Z>        T_DatabaseInfo lDatabaseInfo = new T_DatabaseInfo(mFileName);
Z>        T_DBGoodsReader lDBGoodsReader = new T_DBGoodsReader;
Z>        I_DBAdapter lDBAdapter = null;
        
Z>        if lDatabaseInfo.DBType = Database.DBType1 then
Z>            lDBAdapter = new T_DBAdapter1(mFileName);
Z>        elseif lDatabaseInfo.DBType = Database.DBType2
Z>            lDBAdapter = new T_DBAdapter2(mFileName);
Z>        else
Z>            // исключение "неизвестный тип БД
Z>        endif
        
Z>        lDBGoodsReader.SetAdapter(lAdapter);
Z>        lDBGoodsReader.Init();
Z>        lDBGoodsReader.Read();
        
Z>        // показать на форме список товаров
Z>    }
Z>}
Z>


Z>Напомню, что в конструктор невозможно передать параметры.

Z>Приходится для "Читателя" cделать метод "SetAdapter", чтобы передать ему конкретный объект "Адаптер". И затем вызвать метод "Init".
Z>Я читал, что это анти-паттерн. Но как сделать по другому?

Z>Второй вопрос. Кто (какой объект) должен отвечать за проверку типа базы и создание конкретного Адаптера?

Z>Сейчас это делается в Форме. Но по "теории" форма не должна ничего знать о таких "подробностях". Она должна просто использовать "Читателя".

Так и что вам мешает?
    public partial class Form1 : Form {
        Resolve di = new Resolve1();
        public Form1() {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e) {
            comboBox1.SelectedIndex = 0;
        }
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) {
            int num=comboBox1.SelectedIndex+1;
            di.GetConfig().SelectSource(num);
            update_goods();
        }
        void update_goods() {
            var reader=di.GetGoods();
            var lv = listView1;
            lv.BeginUpdate();
            lv.Items.Clear();
            Goods item;
            while(reader.Next(out item)) {
                var li = new ListViewItem();
                li.Text = item.name;
                li.SubItems.Add(String.Format("{0}", item.amount));
                li.SubItems.Add(String.Format("{0:F2}", item.price));
                lv.Items.Add(li);
            }
            lv.EndUpdate();
        }
    }

    class Goods {
        public string name = "";
        public double amount = 0;
        public double price = 0;
    };
    interface GoodsReader {
        bool Next(out Goods item);
    };
    interface Config {
        void SelectSource(int num);
    }
    interface Resolve {
        GoodsReader GetGoods();
        Config GetConfig();
    }

  Скрытый текст

    class GoodsReader1 : GoodsReader {
        int index = 0;
        Goods[] list = new Goods[]{
            new Goods() { name="good1", amount=4, price=100 },
            new Goods() { name="good2", amount=2, price=150 },
            new Goods() { name="good3", amount=1, price= 75 }
        };
        public bool Next(out Goods item) {
            if (index < 0 || index >= list.Length) { item = null; return false; }
            item = list[index++];
            return true;
        }
    }
    class GoodsReader2 : GoodsReader {
        int index = 0;
        Goods[] list = new Goods[]{
            new Goods() { name="goodA", amount=40, price=120 },
            new Goods() { name="goodB", amount=12, price=130 },
            new Goods() { name="goodC", amount=11, price= 80 },
            new Goods() { name="goodE", amount= 1, price= 30 }
        };
        public bool Next(out Goods item) {
            if (index < 0 || index >= list.Length) { item = null; return false; }
            item = list[index++];
            return true;
        }
    }
    class Config1 : Config {
        Action<int> cb;
        public void SelectSource(int num) { if (cb != null) cb(num); }
        public Config1(Action<int> cb) { this.cb = cb; }
    }

    class Resolve1 : Resolve {
        int source = 1;
        public Config GetConfig() { return new Config1(num => source = num); }
        public GoodsReader GetGoods() {
            if (source == 1) return new GoodsReader1();
            if (source == 2) return new GoodsReader2();
            throw new Exception("invalid data source");
        }

    }

  Скрытый текст
    partial class Form1 {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing) {
            if (disposing && (components != null)) {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent() {
            this.comboBox1 = new System.Windows.Forms.ComboBox();
            this.listView1 = new System.Windows.Forms.ListView();
            this.ColName = new System.Windows.Forms.ColumnHeader();
            this.ColAmount = new System.Windows.Forms.ColumnHeader();
            this.ColPrice = new System.Windows.Forms.ColumnHeader();
            this.SuspendLayout();
            // 
            // comboBox1
            // 
            this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.comboBox1.FormattingEnabled = true;
            this.comboBox1.Items.AddRange(new object[] {
            "Source 1",
            "Source 2"});
            this.comboBox1.Location = new System.Drawing.Point(12, 12);
            this.comboBox1.Name = "comboBox1";
            this.comboBox1.Size = new System.Drawing.Size(260, 21);
            this.comboBox1.TabIndex = 0;
            this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
            // 
            // listView1
            // 
            this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.ColName,
            this.ColAmount,
            this.ColPrice});
            this.listView1.Location = new System.Drawing.Point(12, 48);
            this.listView1.Name = "listView1";
            this.listView1.Size = new System.Drawing.Size(260, 202);
            this.listView1.TabIndex = 1;
            this.listView1.UseCompatibleStateImageBehavior = false;
            this.listView1.View = System.Windows.Forms.View.Details;
            // 
            // ColName
            // 
            this.ColName.Text = "Name";
            // 
            // ColAmount
            // 
            this.ColAmount.Text = "Amount";
            // 
            // ColPrice
            // 
            this.ColPrice.Text = "Price";
            this.ColPrice.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Controls.Add(this.listView1);
            this.Controls.Add(this.comboBox1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.ComboBox comboBox1;
        private System.Windows.Forms.ListView listView1;
        private System.Windows.Forms.ColumnHeader ColName;
        private System.Windows.Forms.ColumnHeader ColAmount;
        private System.Windows.Forms.ColumnHeader ColPrice;
    }
Re[3]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 07.09.23 06:17
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>


Спасибо за такой развернутый ответ!

_>Так и что вам мешает?


Наверно, недостаток опыта и знаний.


_>
_>    public partial class Form1 : Form {
_>        Resolve di = new Resolve1();
_>        public Form1() {
_>            InitializeComponent();
_>        }
...
_>


А разве "хорошо", то что форма знает о конкретном классе "Resolve1"?
Мне кажется, так не должно быть. Разве форма не должна быть "независимой" от конкретной реализации интерфейса Resolve?
Если да, то где создавать объект Resolve1? Может быть в функции Main?
Re[3]: "Гибкий" конструктор для инициализации объекта класса
От: zelenprog  
Дата: 07.09.23 06:21
Оценка:
Здравствуйте, kov_serg!

А можете еще подсказать как "исправить" вот этот код?

Надо сделать примерно следующее:
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();


Как здесь "правильно" выполнить захват ресурсов без метода Init()?
Напомню, что конструктор не может иметь параметров.
Отредактировано 07.09.2023 6:21 zelenprog . Предыдущая версия .
Re[4]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: kov_serg Россия  
Дата: 08.09.23 12:09
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>А разве "хорошо", то что форма знает о конкретном классе "Resolve1"?

Z>Мне кажется, так не должно быть. Разве форма не должна быть "независимой" от конкретной реализации интерфейса Resolve?
Z>Если да, то где создавать объект Resolve1? Может быть в функции Main?
Видимо у вас просто фантазии не хватает пока. Обычно фомру тоже создаёт di
public Form1 GetForm1() {
  var form=new Form1()
  form.di=this;
  // еще чего-нибудь подкручиваем
  return form;
}
Re[4]: "Гибкий" конструктор для инициализации объекта класса
От: kov_serg Россия  
Дата: 08.09.23 12:15
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>А можете еще подсказать как "исправить" вот этот код?


Z>Как здесь "правильно" выполнить захват ресурсов без метода Init()?

1. Унаследоваться от IDisposable (что бы можно было использовать using)
2. Lazy init. Открывать файл по требованию.
3. Пересмотреть логику использования
   using(file=helper.OpenFile(filename)) {
     ...
   }


Z>Напомню, что конструктор не может иметь параметров.

И кому это мешает? Что мешает объекту иметь разные состояния, например с файлом и без файла.
Re[2]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.09.23 12:38
Оценка:
Здравствуйте, zelenprog, Вы писали:

Z>Я читал, что это анти-паттерн. Но как сделать по другому?

Сделайте Adapter параметром метода Init.

Z>Второй вопрос. Кто (какой объект) должен отвечать за проверку типа базы и создание конкретного Адаптера?

Очевидно, AdapterFactory.

Приведённый вами случай отличается от заявленного в начале топика.
Тут у вас инициализация ридера требует фиксированного набора параметров, поэтому ничто не мешает внести их в метод Init (раз уж вы твёрдо решили обойтись без параметров в конструкторе).
По крайней мере у вас чётко будет 2 состояния у объекта: до инициализации и после инициализации; и гарантированно не будет "недоинициализированного" объекта (как могло бы быть, если бы установка отдельных свойств делалась отдельными вызовами со сложным ветвлением).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 14.09.23 07:37
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Приведённый вами случай отличается от заявленного в начале топика.


Отличается только как частное к общему: последний пример — это частный случай той проблемы, которая была описана в первом топике.

S>Тут у вас инициализация ридера требует фиксированного набора параметров, поэтому ничто не мешает внести их в метод Init (раз уж вы твёрдо решили обойтись без параметров в конструкторе).

S>По крайней мере у вас чётко будет 2 состояния у объекта: до инициализации и после инициализации; и гарантированно не будет "недоинициализированного" объекта (как могло бы быть, если бы установка отдельных свойств делалась отдельными вызовами со сложным ветвлением).

Во-о-от. То есть от метода Init в данной ситуации нельзя избавиться. Причем этот метод придется использовать в большинстве классов.
Получается, использование метода Init надо "подчинить" каким-то правилам, чтобы везде его использование в коде было однозначным.
То есть мы приходим к необходимости "оформления" нового "шаблона" создания и инициализации объектов.
Об этом я и спрашивал в первом топике: "есть ли какие-либо устоявшиеся шаблоны\методики\практики более "гибкого" выделения ресурсов в дополнение к конструктору класса"?

Ведь для уничтожения объектов разработали общепринятый IDisposable.
Значит, еще нужен и IInitialaizable.
Re[5]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: zelenprog  
Дата: 15.09.23 10:52
Оценка:
S>Положа руку на сердце, это выглядит как решение искусственно созданной проблемы.

Это не искусственная проблема.
Это особенность "моей" платформы.

S>Вот в дотнете конструктор уже является "методом Init", просто языки вроде C# более-менее следят за тем, чтобы не дать программисту воспользоваться объектом в недоинициализированом состоянии.

S>С точки зрения CLR, к моменту вызова конструктора объект уже полностью сконструирован — у него корректная VMT, и в полях нет никакого булшита (который мог бы помешать работе GC — например, случайные ссылки).
S>То есть авторы шарпа постарались максимально скрыть от пользователя этот зазор между "конструктором" и "Init". А вы зачем-то выпячиваете его наружу.
S>Фактически, тщательно переносите в рантайм проверку, которую прекрасно мог взять на себя компилятор.

>> авторы шарпа постарались максимально скрыть от пользователя этот зазор между "конструктором" и "Init". А вы зачем-то выпячиваете его наружу.

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

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

Приходится "вручную" добавлять эти недостающие возможности ООП.
Re[2]: "Гибкий" конструктор для инициализации объекта класса
От: Sm0ke Россия ksi
Дата: 15.09.23 11:38
Оценка:
Здравствуйте, zelenprog

Можно сделать вспомогательные функции приложения таким образом, чтобы ...
// global function
I_DBAdapter MakeDBAdapter(string pFileName) // 1
{
  I_DBAdapter lDBAdapter = null;
  T_DatabaseInfo lDatabaseInfo = new T_DatabaseInfo(pFileName);
  
  if lDatabaseInfo.DBType = Database.DBType1 then
    lDBAdapter = new T_DBAdapter1(pFileName);
  elseif lDatabaseInfo.DBType = Database.DBType2
    lDBAdapter = new T_DBAdapter2(pFileName);
  else
    // исключение "неизвестный тип БД
  endif
  
  return lDBAdapter;
}

// global function
T_DBGoodsReader MakeDBGoodsReader(string pFileName) // 2
{
  T_DBGoodsReader lDBGoodsReader = new T_DBGoodsReader;
  I_DBAdapter lDBAdapter = MakeDBAdapter(pFileName); // 1
  
  lDBGoodsReader.SetAdapter(lAdapter);
  lDBGoodsReader.Init();
  
  return lDBGoodsReader;
}

... Чтобы их было легче использовать
Class MainForm
{
  string mFileName;
  
  void OnButton_ReadDatabase()
  {
    T_DBGoodsReader lDBGoodsReader = MakeDBGoodsReader(mFileName); // 2
    
    lDBGoodsReader.Read();
    
    // показать на форме список товаров
  }
}


--
Я просто переорганизовал ваш код

Z>
Z>Class MainForm
Z>{
Z>    string mFileName;
    
Z>    void OnButton_ReadDatabase()
Z>    {
Z>        T_DatabaseInfo lDatabaseInfo = new T_DatabaseInfo(mFileName);
Z>        T_DBGoodsReader lDBGoodsReader = new T_DBGoodsReader;
Z>        I_DBAdapter lDBAdapter = null;
        
Z>        if lDatabaseInfo.DBType = Database.DBType1 then
Z>            lDBAdapter = new T_DBAdapter1(mFileName);
Z>        elseif lDatabaseInfo.DBType = Database.DBType2
Z>            lDBAdapter = new T_DBAdapter2(mFileName);
Z>        else
Z>            // исключение "неизвестный тип БД
Z>        endif
        
Z>        lDBGoodsReader.SetAdapter(lAdapter);
Z>        lDBGoodsReader.Init();
Z>        lDBGoodsReader.Read();
        
Z>        // показать на форме список товаров
Z>    }
Z>}
Z>
Отредактировано 15.09.2023 11:40 Sm0ke . Предыдущая версия .
Re: maker generated
От: Sm0ke Россия ksi
Дата: 25.09.23 22:30
Оценка:
Здравствуйте, zelenprog:

У вас есть возможность доработать ваш компилятор?

Скажем чтобы он мог сгенерировать для класса SomeClass
... глобальную функцию SomeClass_make()
... в которой параметры те-же, что и в методе Init() того же класса
а тело функции make примерно такое:
SomeClass_make(int param1, string param2)
{
  SomeClass ret = new SomeClass;
  ret.Init(param1, param2);
  return ret;
}

Чтобы просто форвардить параметры в init

т.е Компилятор знает параметры в init и может их обработать, так?

Но если в классе нет init, то и SomeClass_make() видимо не нужен.
Отредактировано 25.09.2023 22:30 Sm0ke . Предыдущая версия .
Re[6]: "Гибкий" конструктор для инициализации объекта класса (аналог Dispose)
От: Разраб  
Дата: 26.09.23 02:13
Оценка:
Здравствуйте, zelenprog, Вы писали:

S>>Положа руку на сердце, это выглядит как решение искусственно созданной проблемы.


Z>Это не искусственная проблема.

Z>Это особенность "моей" платформы.
1С?
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[2]: maker generated
От: zelenprog  
Дата: 26.09.23 05:43
Оценка:
S>У вас есть возможность доработать ваш компилятор?

Нет такой возможности.

S>Скажем чтобы он мог сгенерировать для класса SomeClass

S>... глобальную функцию SomeClass_make()
S>... в которой параметры те-же, что и в методе Init() того же класса
S>а тело функции make примерно такое:
S>
S>SomeClass_make(int param1, string param2)
S>{
S>  SomeClass ret = new SomeClass;
S>  ret.Init(param1, param2);
S>  return ret;
S>}
S>

S>Чтобы просто форвардить параметры в init

Так это просто Фабрика?
Re[3]: maker generated
От: Sm0ke Россия ksi
Дата: 26.09.23 12:10
Оценка:
Здравствуйте, zelenprog, Вы писали:

S>>Скажем чтобы он мог сгенерировать для класса SomeClass

S>>... глобальную функцию SomeClass_make()
S>>... в которой параметры те-же, что и в методе Init() того же класса
S>>а тело функции make примерно такое:
S>>
S>>SomeClass_make(int param1, string param2)
S>>{
S>>  SomeClass ret = new SomeClass;
S>>  ret.Init(param1, param2);
S>>  return ret;
S>>}
S>>

S>>Чтобы просто форвардить параметры в init

Z>Так это просто Фабрика?


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