Как покрасивше без “if”?
От: Hоmunculus  
Дата: 14.01.26 12:27
Оценка: +1 :))) :)
Есть базовый класс A. Есть наследник В. И есть наследник наследника С.
Есть некая структура данный. Она парсится из файла.
Если в этой структуре все поля после парсинга заполнились — то на основе этой структуры создается класс С.
Если каких-то полей нет, но чуть больше чем совсем минимум — то создается класс В
А если совсем только базовый минимум полей заполнились из файла, то создается объекта класса А.

Ясно, что тупым «if» проверяя поля структуры можно проверять какой именно класс создавать. Но что-то некрасиво как-то. Можно как-то стильно модно молодежно, С++-но и по последним стандартам с сотней шаблонов и SFINAE разрулить создание нужного класса на основе прочитанной структуры?
Re: Как покрасивше без “if”?
От: imh0  
Дата: 14.01.26 12:36
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Ясно, что тупым «if» проверяя поля структуры можно проверять какой именно класс создавать. Но что-то некрасиво как-то. Можно как-то стильно модно молодежно, С++-но и по последним стандартам с сотней шаблонов и SFINAE разрулить создание нужного класса на основе прочитанной структуры?


Нельзя такие if-ы.
Если потребность в таких ифах в конструкторе возникает, что это симптом плохого дизайна и нарушение С++ стайла.
Так пишут на СИ.
Re[2]: Как покрасивше без “if”?
От: Hоmunculus  
Дата: 14.01.26 12:42
Оценка: :)
Здравствуйте, imh0, Вы писали:

I>Здравствуйте, Hоmunculus, Вы писали:


H>>Ясно, что тупым «if» проверяя поля структуры можно проверять какой именно класс создавать. Но что-то некрасиво как-то. Можно как-то стильно модно молодежно, С++-но и по последним стандартам с сотней шаблонов и SFINAE разрулить создание нужного класса на основе прочитанной структуры?


I>Нельзя такие if-ы.

I>Если потребность в таких ифах в конструкторе возникает, что это симптом плохого дизайна и нарушение С++ стайла.
I>Так пишут на СИ.

А ты вопрос-то мой прочитал ли?
Re[3]: Как покрасивше без “if”?
От: imh0  
Дата: 14.01.26 12:55
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>А ты вопрос-то мой прочитал ли?


Не... )
Помоему какой-то патерн разпознался. Сам уже перечитал и понял что у тебя совсем про другое.

Ну тут скорее нужен парсер в обьект "результат парсинга" и потом уже какой-то конвертор.
Но вообще тут точно нужен какой-то правльный дизайн наследования.)
Re: Как покрасивше без “if”?
От: pilgrim_ Россия  
Дата: 14.01.26 15:19
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Есть базовый класс A. Есть наследник В. И есть наследник наследника С.

H>Есть некая структура данный. Она парсится из файла.
H>Если в этой структуре все поля после парсинга заполнились — то на основе этой структуры создается класс С.
H>Если каких-то полей нет, но чуть больше чем совсем минимум — то создается класс В
H>А если совсем только базовый минимум полей заполнились из файла, то создается объекта класса А.

Если это твой внутренний утилитарный формат, который сам формируешь и сохраняешь (сериализуешь) объекты тех же типов, которые затем читаешь, то самое простое указывать для сохраняемых данных тип к которому они принадлежат (type discriminator) — строка, или числовой идентификатор. Соотв. при чтении (десериализации) на основании этого идентификатора создаёшь нужный тип A/B/C.
Re[2]: Как покрасивше без “if”?
От: Hоmunculus  
Дата: 14.01.26 15:21
Оценка:
Здравствуйте, pilgrim_, Вы писали:

_>Соотв. при чтении (десериализации) на основании этого идентификатора создаёшь нужный тип A/B/C.


И в этом моменте будет «if»
Re[3]: Как покрасивше без “if”?
От: pilgrim_ Россия  
Дата: 14.01.26 15:29
Оценка: +1
Здравствуйте, Hоmunculus, Вы писали:

H>Здравствуйте, pilgrim_, Вы писали:


_>>Соотв. при чтении (десериализации) на основании этого идентификатора создаёшь нужный тип A/B/C.


H>И в этом моменте будет «if»


Это у же другой "if" , там может быть и switch, и словарь, в котором каждому идентифкатору типа будет соотв. своя фабрика его создания, предварительно в этом словаре могут быть зарегистрированны поддерживаемые типы.
Re[4]: Как покрасивше без “if”?
От: Hоmunculus  
Дата: 14.01.26 15:34
Оценка:
Здравствуйте, pilgrim_, Вы писали:

Нет. Некрасиво. Лишняя сущность без необходимости.
Необходимо именно как-то уже прочитанную структуру обернуть соответствующим ее заполнению классом.

Это сделать элементарно. Задача — без проверок. Перевод всех проверок на сравнение иднетиыткатора — ничего не меняет
Re[5]: Как покрасивше без “if”?
От: pilgrim_ Россия  
Дата: 14.01.26 16:06
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Нет. Некрасиво. Лишняя сущность без необходимости.


Т.к. конкретный тип (его идентификатор), которому соотв. данные, известен только в динамике (читаешь из файла), то в любом случае будет if/switch/фабрика для его создания

H>Необходимо именно как-то уже прочитанную структуру обернуть соответствующим ее заполнению классом.


Дык это уже другая задача — инициализация конкретного объекта конкретного типа (созданного на предыдущем шаге), тут может помочь виртуальная инициализацияя, псевдокод:

struct A {
    virtual void initialize(const твоя_прочитанная_структура& data) {
        //тут A знает какие поля брать из data
    }
}

struct B : A {
    void initialize(const твоя_прочитанная_структура& data) override {
        A::initialize(data);
        //тут B знает какие поля брать из data
    }
}

struct C : B {
    void initialize(const твоя_прочитанная_структура& data) override  {
        B::initialize(data);
        //тут C знает какие поля брать из data
    }
}

...

A* create_instance(const твоя_прочитанная_структура& data) {
    A* obj = ...;
    return obj;
}

...
твоя_прочитанная_структура data = read_data(...);
A* obj = create_instance(data);
obj->initialize(data);


H>Это сделать элементарно. Задача — без проверок. Перевод всех проверок на сравнение иднетиыткатора — ничего не меняет


или я опять не понял что ты хочешь?
Re: Как покрасивше без “if”?
От: gyraboo  
Дата: 14.01.26 16:22
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Есть базовый класс A. Есть наследник В. И есть наследник наследника С.

H>Есть некая структура данный. Она парсится из файла.
H>Если в этой структуре все поля после парсинга заполнились — то на основе этой структуры создается класс С.
H>Если каких-то полей нет, но чуть больше чем совсем минимум — то создается класс В
H>А если совсем только базовый минимум полей заполнились из файла, то создается объекта класса А.

H>Ясно, что тупым «if» проверяя поля структуры можно проверять какой именно класс создавать. Но что-то некрасиво как-то. Можно как-то стильно модно молодежно, С++-но и по последним стандартам с сотней шаблонов и SFINAE разрулить создание нужного класса на основе прочитанной структуры?


Сам по себе IF в коде не является злом. Особенно если IFы сидят внутри (инкапсулированы, спрятаны от клиентского кода).
А вот если IFами обвешана бизнес-логика (т.н. "клиентский код"), это уже антипаттерн.
По GoF правильным считается архитектура, когда клиентский код использует тип A (за которым в рантайме, благодаря ООП, может прятаться любой его подкласс — B или С). Для создания экземпляра типа A клиентский код вызывает парсер, который возвращает тип A:

parser {
A parse(filename) {}
}



Внутри парсера инкапсулирована логика, которая парсит файл и вызывает GoF-паттерн "фабрика" (сигнатура вызова тоже возвращает тип A, например: A createByparams(params)), передавая ей распарсенные параметры.
Внутри фабрики инкапсулирована логика, которая в зависимости от полноты параметров, с помощью IF, инстанцирует экземпляр A, B или С. В коде фабрики IF не запрещены, т.к. это не бизнес-логика, а вспомогательная логика.

В чём профит такого подхода: в коде бизнес-логики мы соблюдаем принципы SOLID, т.к. бизнес-логика оперирует только типом А. Код бизнес-логики становится простым (не обременённым IFами), расширяемым (для добавления нового подтипа А ты просто добавляешь новый подкласс, остальной код, особенно код бизнес-логики, ты не трогаешь, тем самым минимизируя вероятность регрессионных багов) и более надёжным, чем "матрёшка из IFов".

Но и правильно спроектировать такой код сложнее. Например, тебе нужно соблюсти принцип подстановки Лисковы из SOLID, т.е. чтобы бизнес-код оперировал А, даже если в рантайме за ним прячется B или С, и клиент не должен анализировать реальный подтип А, т.е. в бизнес-коде запрещены такие конструкции: if (a instanceof C) then...
Это иногда бывает не так просто.
Отредактировано 14.01.2026 16:24 gyraboo . Предыдущая версия .
Re[6]: Как покрасивше без “if”?
От: Hоmunculus  
Дата: 14.01.26 16:23
Оценка:
Здравствуйте, pilgrim_, Вы писали:

Не понял где в твоем коде какой именно класс создается?
То что initialize виртуальная — это понятно. И что на базе структуры можно все три класса создать — тоже понятно. Как узнать максимально возможный класс без if?
Re[7]: Как покрасивше без “if”?
От: Stanislav V. Zudin Россия  
Дата: 14.01.26 16:36
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Не понял где в твоем коде какой именно класс создается?

H>То что initialize виртуальная — это понятно. И что на базе структуры можно все три класса создать — тоже понятно. Как узнать максимально возможный класс без if?

Например, попытаться в цикле создать объект. От С к А.


unique_ptr<A> ctorA()
{
   return make_unique<A>();
}
unique_ptr<A> ctorB()
{
   return make_unique<B>();
}
unique_ptr<A> ctorC()
{
   return make_unique<C>();
}


unique_ptr<A> create_instance(const твоя_прочитанная_структура& data) 
{
   for(auto ctor : {ctorC, ctorB, ctorA})
   {
      unique_ptr<A> obj = ctor(); // создал экземпляр нужного класса
      if (obj->initialize(data))  // если он сумел себя полностью инициализировать
         return obj;              // отдаём наружу

      // иначе переходим к классу "попроще".
   }
   
   return {}; // Вообще никого не удалось сконструировать - фигня какая-то зачиталась
}
_____________________
С уважением,
Stanislav V. Zudin
Re[7]: Как покрасивше без “if”?
От: pilgrim_ Россия  
Дата: 14.01.26 16:56
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Здравствуйте, pilgrim_, Вы писали:


H>Не понял где в твоем коде какой именно класс создается?


Вот тут на основании данных в data определяется, какой тип нужно создать:

_>A* create_instance(const твоя_прочитанная_структура& data) {

_> A* obj = ...;
_> return obj;
_>}

более детально, пример, псевдокод:


enum eTypeId {
    a, b, c
};

std::map<eTypeId, std::function<A*()>> factory;

void init_factory() {
    factory[eTypeId::a] = [] ()  { return new A(); };
    factory[eTypeId::b] = [] ()  { return new B(); };
    factory[eTypeId::c] = [] ()  { return new C(); };
}

A* create_instance(const my_data& data) {
    //тут по хорошему надо поверить, что есть фабрика для данного типа
    A* obj =  factory[data.typeId]();
    return obj;
}

...
init_factory();

...
твоя_прочитанная_структура data = read_data(...);
A* obj = create_instance(data);
obj->initialize(data);


H>То что initialize виртуальная — это понятно. И что на базе структуры можно все три класса создать — тоже понятно. Как узнать максимально возможный класс без if?


В динамике никак.
Если идея с идентификатором типа не нравится (не хочешь/можещь менять существующий формат данных), то можно определить набор требуемых полей для каждого типа (они уже есть, судя по изначальному посту), на основе их наличия сформировать свой динамический идентификатор типа (напр. в виде строки), а далее см. выше (factory, create_instance), ну без сравнений всё-равно не обойтись.
Re[8]: Как покрасивше без “if”?
От: pilgrim_ Россия  
Дата: 14.01.26 16:58
Оценка: :)
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>

SVZ>      if (obj->initialize(data))  // если он сумел себя полностью инициализировать
SVZ>         return obj;              // отдаём наружу

SVZ>


тут ты и попался
Re[9]: Как покрасивше без “if”?
От: Stanislav V. Zudin Россия  
Дата: 14.01.26 17:11
Оценка: :)))
Здравствуйте, pilgrim_, Вы писали:

SVZ>>

SVZ>>      if (obj->initialize(data))  // если он сумел себя полностью инициализировать
SVZ>>         return obj;              // отдаём наружу

SVZ>>


_>тут ты и попался


а я, как будто, из последних сил

unique_ptr<A> create_instance(const твоя_прочитанная_структура& data) 
{
   for(auto ctor : {ctorC, ctorB, ctorA})
   {
      try
      {
         unique_ptr<A> obj = ctor(); // создал экземпляр нужного класса
         obj->initialize(data);      // если он сумел себя полностью инициализировать
         return obj;                 // отдаём наружу
      }
      catch(IncompleteData&)
      {
         // иначе переходим к классу "попроще".
      }
      catch(...)
      {
         throw;
      }
   }
   
   return {}; // Вообще никого не удалось сконструировать - фигня какая-то зачиталась
}


Где ты видишь if'ы?
_____________________
С уважением,
Stanislav V. Zudin
Re[8]: Как покрасивше без “if”?
От: T4r4sB Россия  
Дата: 14.01.26 17:17
Оценка: +1
SVZ> unique_ptr<A> obj = ctor(); // создал экземпляр нужного класса
SVZ> if (obj->initialize(data)) // если он сумел себя полностью инициализировать
SVZ> return obj; // отдаём наружу

Я бы тело цикла заменил на
if (unique_ptr<A> obj  = ctor(data); obj)
  return obj;
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[9]: Как покрасивше без “if”?
От: Stanislav V. Zudin Россия  
Дата: 14.01.26 17:22
Оценка:
Здравствуйте, T4r4sB, Вы писали:


SVZ>> unique_ptr<A> obj = ctor(); // создал экземпляр нужного класса

SVZ>> if (obj->initialize(data)) // если он сумел себя полностью инициализировать
SVZ>> return obj; // отдаём наружу

TB>Я бы тело цикла заменил на

TB>
TB>if (unique_ptr<A> obj  = ctor(data); obj)
TB>  return obj;
TB>


+1
В твоём варианте можно будет разбирать данные _до_ конструирования объекта.
Мелочь, но может оказаться полезной.
_____________________
С уважением,
Stanislav V. Zudin
Re: Как покрасивше без “if”?
От: Doom100500 Израиль  
Дата: 15.01.26 06:45
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Есть базовый класс A. Есть наследник В. И есть наследник наследника С.

H>Есть некая структура данный. Она парсится из файла.
H>Если в этой структуре все поля после парсинга заполнились — то на основе этой структуры создается класс С.
H>Если каких-то полей нет, но чуть больше чем совсем минимум — то создается класс В
H>А если совсем только базовый минимум полей заполнились из файла, то создается объекта класса А.

Разве это не Factory?
Там в любом случае будут ветвления на этапе создания.
Спасибо за внимание
Re[2]: Как покрасивше без “if”?
От: Hоmunculus  
Дата: 15.01.26 06:52
Оценка:
Здравствуйте, Doom100500, Вы писали:

D>Разве это не Factory?

D>Там в любом случае будут ветвления на этапе создания.

Ну по сути да, но так как случай не общий, а вполне конкретный и то, что у нас более расширенные данные должны порождать более расширенный класс — это ж как бы в сути наследования, думал мож какой отдельный специальный для такого механизм есть.
Re[3]: Как покрасивше без “if”?
От: Doom100500 Израиль  
Дата: 15.01.26 07:01
Оценка:
Здравствуйте, Hоmunculus, Вы писали:


H>Ну по сути да, но так как случай не общий, а вполне конкретный и то, что у нас более расширенные данные должны порождать более расширенный класс — это ж как бы в сути наследования, думал мож какой отдельный специальный для такого механизм есть.


Эээ. Композиция? Правда кода больше писать придётся в самих классах. Возврат нужных инерфейсов, методы их запроса.
Спасибо за внимание
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.