Re[5]: бессмысленные интерфейсы
От: B0FEE664  
Дата: 19.02.22 18:12
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>>Для представления древовидных структур, с коллекциями элементов того же типа, что и корневой элемент?

AG>>Когда в корневом элементе требуется иметь коллекцию child-элементов.

BFE>>Не вижу препятствий.


AG>Элемент дерева, как логично я заметил выше, один и тот же.

AG>Для каких-то других элементов — он является parent-ом.
AG>Для других — чайлдом.
AG>Ссылочку на этот чайлд хранит родитель (parent) в своей коллекции.
Тогда смотрите ответ Точки.

AG>То, что Вы писали — это два разных класса.

AG>Я бы предложил так:

AG>
AG>class INode // Интерфейс узла:
AG>{
AG>public:
AG>    virtual std::string GetNameOfNode() const = 0;
AG>    virtual std::vector<std::shared_ptr<INode>> GetVectOfChilds() const = 0;
AG>    virtual void AddNode(std::shared_ptr<INode> p) = 0;
AG>};
AG>


AG>
AG>class Node : public INode // Класс реализации узла (header):
AG>{
AG>public:
AG>    Node(const std::string& sNameOfNode); // C-tor
AG>    ~Node(); // D-tor
AG>    std::string GetNameOfNode() const override;
AG>    std::vector<std::shared_ptr<INode>> GetVectOfChilds() const override;
AG>    void AddNode(std::shared_ptr<INode> p) override;
AG>};
AG>


AG>Здесь и у Вас, и у меня — две сушности.

AG>Но у меня — универсальный узел и интерфейс к нему.
AG>Именно наличие программного интерфейса — позволяет уменьшить связанность (coupling) в проекте.
AG>За счёт этого, получим упрощение архитектуры и уменьшение количества ошибок.

Для того, чтобы уменьшить связность, виртуальные методы не нужны:
class SubNode;

class Node
{
public:
    Node(const std::string& sNameOfNode); // C-tor
   ~Node(); // D-tor
    std::string GetNameOfNode() const;
    const std::vector<SubNode>& GetVectOfChilds() const;
    void AddNode(SubNode p);
private:
    std::vector<SubNode> m_SubNodes;
};

class SubNode // Интерфейс узла:
{
public:
    std::string                 GetNameOfNode() const     {  return m_node->GetNameOfNode();  }
    const std::vector<SubNode>& GetVectOfChilds() const   {  return m_node->GetNameOfNode();  }
    void                        AddNode(SubNode node)     {  return m_node->AddNode(node);    }        
public:
    std::shared_ptr<Node> m_node;
};

AG>Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее.
Нет. Если вы хотите, чтобы элемент дерева был один и тот же, то он и должен быть один и тот же. Хотите уменьшить связность, то можно сделать, как я показал. Просто и понятно. А интерфейсы нужны там, где есть коллекции разных типов обрабатываемых в едином конвейере. Скажем если листья дерева, корень дерева и промежуточные узлы хранят разную информацию, то вот тогда следует применить интерфейс в виде класса с абстрактными методами.

AG>Кроме того, имеется заложенная база — как для развития (новые типы узлов),

Вот! Я видел очень много проектов, где была заложена база для развития и это развитие никогда не наступало. Более того, через несколько лет оказывалось, что развивать надо совсем не то, что предполагалось изначально, а там, где оно предполагалось развития никакого не надо. В результате вместо упрощения имеем усложнение из-за дополнительных классов, которые никому не нужны.

AG>так и для тестирования основы нашего проекта.

Про связь с тестированием я не понял.
И каждый день — без права на ошибку...
Re[3]: бессмысленные интерфейсы
От: B0FEE664  
Дата: 19.02.22 18:23
Оценка:
Здравствуйте, elmal, Вы писали:

BFE>>В конторе, где я сейчас работаю, прямо в правилах кодирования есть пункт запрещающий создавать интерфейс если его реализация будет ровно в одном классе. Причин не знаю.

E>Причина проста. Кода должно быть ровно столько, сколько необходимо. Минимальное количество кода — имеем одну реализацию. Потребовалась другая — выделяем интерфейс. Выделяем когда потребуется, а не потому, что так "положено".
E>Меньше кода — это легче читается, легче читается — проще понимание, проще понимание — делаешь меньше ошибок, делаешь меньше ошибок — задача быстрее будет сдана. Меньше кода — это более легкие и более быстрые изменения, это меньше проблемами с мержем с другими и т.д. При развитии можно все сложности во всякие библиотеки и утилиты добавлять, а основной код должен быть минимальным по объему. А библиотеки и утилиты — там вообще никаких классов, там тупо набор легко тестируемых функций.

Какой-то странный подход.
Во-первых: легче читается и понимается тот код, который написан в привычном читателю стиле с использованием известных читателю приёмов. (Это, конечно, при отсутствии не выраженных в коде зависимостей).
Во-вторых: минимальное количества кода — это что? Например, если завести глобальную переменную для индекса всех циклов программы, то это ведь уменьшит код. Пример, конечно, абсурдный, но я такое видел.
И каждый день — без права на ошибку...
Re[6]: бессмысленные интерфейсы
От: AlexGin Беларусь  
Дата: 19.02.22 19:04
Оценка:
Здравствуйте, уважаемый B0FEE664, Вы писали:

BFE>Для того, чтобы уменьшить связность, виртуальные методы не нужны:

BFE>
BFE>class SubNode;

BFE>class Node
BFE>{
BFE>public:
BFE>    Node(const std::string& sNameOfNode); // C-tor
BFE>   ~Node(); // D-tor
BFE>    std::string GetNameOfNode() const;
BFE>    const std::vector<SubNode>& GetVectOfChilds() const;
BFE>    void AddNode(SubNode p);
BFE>private:
BFE>    std::vector<SubNode> m_SubNodes;
BFE>};

BFE>class SubNode // Интерфейс узла:
BFE>{
BFE>public:
BFE>    std::string                 GetNameOfNode() const     {  return m_node->GetNameOfNode();  }
BFE>    const std::vector<SubNode>& GetVectOfChilds() const   {  return m_node->GetNameOfNode();  }
BFE>    void                        AddNode(SubNode node)     {  return m_node->AddNode(node);    }        
BFE>public:
BFE>    std::shared_ptr<Node> m_node;
BFE>};
BFE>


Прекрасно!
У нас уже (с Вашей подачи) — две равнозначные сущности.
Там, напомним, где в принципе достаточно одной (плюс вспомогательная: интерфейс).
Я вот посмотрел и подумал — есть Node, есть SubNode.
Есть Forward Declaration, которое так и не решило проблему перекрёстных ссылок.

Зачем так сложно?
Почему так не-KISS-ло всё накручено?

Неудивительно, что это всё даже не компилируется

AG>>так и для тестирования основы нашего проекта.

BFE>Про связь с тестированием я не понял.

Вот насчёт тестов:
https://chromium.googlesource.com/external/github.com/google/googletest/+/refs/tags/release-1.8.0/googlemock/docs/ForDummies.md
https://chromium.googlesource.com/external/github.com/google/googletest/+/refs/tags/release-1.8.0/googlemock/docs/FrequentlyAskedQuestions.md#how-am-i-supposed-to-make-sense-of-these-horrible-template-errors
Re[13]: бессмысленные интерфейсы
От: · Великобритания  
Дата: 19.02.22 19:39
Оценка:
Здравствуйте, elmal, Вы писали:

E>У тебя неявные зависимости тоже есть, например Locale. Запускаешь в одном окружении, у тебя так даты выводятся и запятая отображается точкой, а в другом окружении наоборот.

Обычно я избегаю такие моменты типа системной локали или таймзоны. Все функции позволяют передавать локаль явно. Если зависимость от системных настроек, то это может ломать, например, тесты, когда они выполняются у разных девов на их компе.
Системная локаль/tz инжектится в wiring root где-нибдуь в main уже самого приложения.

E>Ну и мой изначальный пост был немного ошибочным, я просто неверно воспринял выражение ConfigHelper().getFooValue(). Воспринял как new ConfigHelper().getFooValue(), слишком привыкнув к Java конвенции именовать функции и методы начиная со строчной буквы. А любовь ко всяким new для выполнения простейших действий даже в стандартной библиотеке меня несколько огорчает, а многие подобные подходы и в своем коде используют, что меня сильно печалит.

Не очень ясно что именно огорчает, можно пример? В случае какого-нибудь там new ArrayList я не вижу никаких проблем.
С new ConfigHelper() неясно откуда конфиг собственно браться будет, не из глобальной же переменной опять, поэтому инжектим зависимость.

E>·>"Ceylon Stable release 1.3.3 / August 21, 2017; 4 years ago", ну да, ничего страшного.

E>Я как бы в курсе . На деле весьма хороший был язык, вроде как полностью перекрывается по фичам scala 3, которую все никак не соберусь попробовать. Некоторых фичей сейчас реально не хватает.
Да не знаю, современная java делает ненужной все эти языки. По крайней мере, в подавляющем большинстве случаев.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[14]: бессмысленные интерфейсы
От: elmal  
Дата: 19.02.22 20:02
Оценка:
Здравствуйте, ·, Вы писали:

·>Не очень ясно что именно огорчает, можно пример? В случае какого-нибудь там new ArrayList я не вижу никаких проблем.

·>С new ConfigHelper() неясно откуда конфиг собственно браться будет, не из глобальной же переменной опять, поэтому инжектим зависимость.
Меня в свое время дико бомбил класс Calendar, когда чтоб сконструировать дату приходилось делать инстанс календаря и потом там делать кучу всякого, каждый раз в новом проекте приходилось лисапеды писать чтоб нормально работать с датой. Сейчас конечно с LocalDate все поприятнее.

·>Да не знаю, современная java делает ненужной все эти языки. По крайней мере, в подавляющем большинстве случаев.

Дело привычки. В современной Java по прежнему нет Nullability, и это физически не исправить из за необходимости держать обратную совместимость. Привык если к тому, что String и String? это существенная разница — уже в чистой Java дискомфорт будет. top level функций по прежнему нет, как и extension функций — если привык, отсутствие вызывает дискомфорт. Ну и система типов — Union Types, Intersection Types — когда ими проникся, их отсутствие вызывает дискомфорт, реально классная штука, которая сейчас в Scala 3 насколько я понимаю только есть. И куча всего еще вроде необходимости операции new, точки с запятой и т.д — когда привыкаешь к хорошему, уже стандартный синтаксис выглядит громоздко. Java конечно лучше становится, но медленно.
Re[15]: бессмысленные интерфейсы
От: · Великобритания  
Дата: 19.02.22 21:26
Оценка:
Здравствуйте, elmal, Вы писали:

E>·>Не очень ясно что именно огорчает, можно пример? В случае какого-нибудь там new ArrayList я не вижу никаких проблем.

E>·>С new ConfigHelper() неясно откуда конфиг собственно браться будет, не из глобальной же переменной опять, поэтому инжектим зависимость.
E>Меня в свое время дико бомбил класс Calendar, когда чтоб сконструировать дату приходилось делать инстанс календаря и потом там делать кучу всякого, каждый раз в новом проекте приходилось лисапеды писать чтоб нормально работать с датой. Сейчас конечно с LocalDate все поприятнее.
Эх, это да. Это очень старинный класс и проектировался в своё время в своих условиях.
Ещё меня checked exceptions достают, особенно с лямбдами.

E>·>Да не знаю, современная java делает ненужной все эти языки. По крайней мере, в подавляющем большинстве случаев.

E>Дело привычки. В современной Java по прежнему нет Nullability, и это физически не исправить из за необходимости держать обратную совместимость. Привык если к тому, что String и String? это существенная разница — уже в чистой Java дискомфорт будет.
Согласен. Впрочем, если очень хочется, есть https://checkerframework.org/manual/#nullness-checker

E>top level функций по прежнему нет, как и extension функций — если привык, отсутствие вызывает дискомфорт.

Хм... в смысле реверсить композицию? Вместо f(g(h(x))) писать x.h().g().f()? Ну... не знаю, да, может дело привычки.
А в скале операторы достают. Иероглифы какие-то. Написан какой-нибудь <: и догадайся что за х и как такое гуглить.

E>Ну и система типов — Union Types, Intersection Types — когда ими проникся, их отсутствие вызывает дискомфорт, реально классная штука, которая сейчас в Scala 3 насколько я понимаю только есть.

Ну да... прикольно. В typescript такое есть, удобно.
Кстати, появились sealed classes, но честно говоря пока не придумал когда их надо использовать на практике.

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

Всё равно большинство кода Идея за тебя пишет.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[16]: бессмысленные интерфейсы
От: elmal  
Дата: 20.02.22 05:47
Оценка: +1
Здравствуйте, ·, Вы писали:

·>Кстати, появились sealed classes, но честно говоря пока не придумал когда их надо использовать на практике.

Так каноничный пример — стейт машина и в зависимости от состояния там разные значения, и это как альтернатива енумам. Условно говоря — состояние в очереди, там дата постановки в очередь, состояние в работе — там добавляется дата запуска и логи выполнения, состояние завершено, там есть дата завершения, логи, результат и дата старта, состояние ошибка с логами, датой ошибки, причиной ошибка. Очень удобно и красиво. Там где енум напрагмвается, сразу имеет смысл подумать над sealed классами, возможно имеет смысл.
Re[2]: бессмысленные интерфейсы
От: Shtole  
Дата: 20.02.22 06:42
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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

C>>В этом есть какой-то тайный смысл, или они просто идиоты?

BFE>В конторе, где я сейчас работаю, прямо в правилах кодирования есть пункт запрещающий создавать интерфейс если его реализация будет ровно в одном классе. Причин не знаю.


А можно прямую цитату? Выделенное можно трактовать по-разному.

P.S. Объясню, что имеется в виду. Есть ситуации, когда программист предполагает, что реализация будет только одна. И есть ситуации, когда программист хочет подчеркнуть, что реализаций возможно много, но нам сейчас нужна только одна. Интересно узнать, какой из этих случаев подразумевается. Во втором случае часто говорят «ЯГНИ!», что лично я считаю неверным. (По крайней мере, когда я вижу, что в результате получается).
Do you want to develop an app?
Отредактировано 22.02.2022 7:27 Shtole . Предыдущая версия .
Re[11]: бессмысленные интерфейсы
От: Ночной Смотрящий Россия  
Дата: 20.02.22 09:04
Оценка:
Здравствуйте, elmal, Вы писали:

N>>Кто сказал? Конфиг это контекст. Контекстов может быть несколько. Например, несколько экземпляров стека протоколов на разных IP адресах или портах, с разными свойствами.

E>Я вообще то считал, что конфиг — это такое условно неизменное что то, а контекст — это что то постоянно меняющееся, соответственно это все совершенно разные вещи.

Давай я тебе проиллюстрирую.
Предположим, написал ты некий компонент, который умеет что то доставать и что то класть в БД. Коннект к БД прописываешь в статическом конфиге.
А теперь прибегает к тебе продакт и просит сделать утилиту для переноса данных из одной БД в другую. С использованием твоего компонента. Осознал проблему?
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[11]: бессмысленные интерфейсы
От: Ночной Смотрящий Россия  
Дата: 20.02.22 09:07
Оценка:
Здравствуйте, elmal, Вы писали:

E>Не всегда нужны классы, иногда для простоты и удобства гораздо проще до черта логики сделать обычными top level функциями.


Ага. Называется "херак-херак и в продакшн".

E> Во вторых, если классы все таки нужны, без DI фреймворков ты задолбаешься этот конфиг в каждый класс прокидывать и инициализировать.


Зависит от количества классов и их структуры. Никто не мешает использовать некий RootContext, тогда протаптывать нужно будет только в один класс.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[13]: бессмысленные интерфейсы
От: Ночной Смотрящий Россия  
Дата: 20.02.22 09:12
Оценка:
Здравствуйте, elmal, Вы писали:

E>У тебя неявные зависимости тоже есть, например Locale.


Не знаю как в джаве, а в дотнете локаль это не static, а thread static. Это уже несколько иной коленкор, хотя тоже не фонтан.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[8]: бессмысленные интерфейсы
От: Ночной Смотрящий Россия  
Дата: 20.02.22 09:12
Оценка:
Здравствуйте, baxton_ulf, Вы писали:

_>да я что спорю что-ли? все правильно, все рефакторится. но если бы тесты писались вместе с кодом то рефакторить не пришлось бы.


Лучше не придумывать будущие требования, а писать код под требования здесь и сейчас, и при этом максимально удобный для рефакторинга. Проверено десятками, если не сотнями тысяч проектов.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[12]: бессмысленные интерфейсы
От: elmal  
Дата: 20.02.22 09:17
Оценка: +1
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Осознал проблему?

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

Если изначально писалась простенькая утилитка для скриптинга на нормальном языке, то по умолчанию будет статический конфиг, возможно даже хардкодом для скорости. Если же вдруг задача оказывается типичной и захочется именно повторного использования — это все очень быстро рефакторится, и уже будет и класс, и к классу привязан конфиг в конструкторе плюс там будут параметры по умолчанию — никакой проблемы, рефакторится все очень быстро.
Re[4]: бессмысленные интерфейсы
От: Ночной Смотрящий Россия  
Дата: 20.02.22 09:18
Оценка:
Здравствуйте, Тёмчик, Вы писали:

C>>C#

Тё>Это многое обьясняет

Например?
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[2]: бессмысленные интерфейсы
От: Ночной Смотрящий Россия  
Дата: 20.02.22 09:18
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>В конторе, где я сейчас работаю, прямо в правилах кодирования есть пункт запрещающий создавать интерфейс если его реализация будет ровно в одном классе. Причин не знаю.


Скорее всего причина в каком то заваленном из-за возомнивших себя крутыми архитектами разработчиков. Мне один такой проект как то пришлось поддерживать некоторое время.
Да и сейчас мне на ревью молодежь иногда пишет — а почему ты сервис не через интерфейс в DI протащил? У многих свеженанятых прям императив — если есть сервис в DI, у него обязательно должен быть интерфейс, даже если сервис internal и никаких моков к нему не планируется.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[13]: бессмысленные интерфейсы
От: Ночной Смотрящий Россия  
Дата: 20.02.22 09:21
Оценка:
Здравствуйте, elmal, Вы писали:

НС>>Осознал проблему?

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

Или нет.

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


Вот проблема как раз в том, что глобальные переменные это очень больно для рефакторинга, ибо потенциально может неявно затронуть непредсказуемое количество кода.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[14]: бессмысленные интерфейсы
От: · Великобритания  
Дата: 20.02.22 10:36
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

E>>У тебя неявные зависимости тоже есть, например Locale.

НС>Не знаю как в джаве, а в дотнете локаль это не static, а thread static. Это уже несколько иной коленкор, хотя тоже не фонтан.
В java есть дефолтная локаль виртуальной машины (т.е. глобальная переменная, проставляется из операционки, но можно поменять) и плюс все локаль-зависимые методы могут принимать локаль явно. Т.е. например string.toLowerCase() == string.toLowerCase(Locale.getDefault())
Thread static — это вредительство какое-то в нашу эпоху асинков...
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[15]: бессмысленные интерфейсы
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 20.02.22 10:50
Оценка:
Здравствуйте, ·, Вы писали:

·>Thread static — это вредительство какое-то в нашу эпоху асинков...


А переключатель контекста асинков разве ещё не способен потянуть за собой нужные параметры?
The God is real, unless declared integer.
Re[16]: бессмысленные интерфейсы
От: · Великобритания  
Дата: 20.02.22 11:06
Оценка:
Здравствуйте, netch80, Вы писали:

N>·>Thread static — это вредительство какое-то в нашу эпоху асинков...

N>А переключатель контекста асинков разве ещё не способен потянуть за собой нужные параметры?
Может и протягивает там где протягивается... Но думаю это легко может поломаться, если использовать какую-нибудь необычную передачу данных между тредами. В любом случае, по-моему это будет очень весело разбираться если что-то где-то не так протащится. Вместо одной глобальной переменной, у нас теперь неожиданно целая их коллекция.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[15]: бессмысленные интерфейсы
От: Ночной Смотрящий Россия  
Дата: 20.02.22 11:19
Оценка:
Здравствуйте, ·, Вы писали:

·>В java есть дефолтная локаль виртуальной машины (т.е. глобальная переменная, проставляется из операционки, но можно поменять) и плюс все локаль-зависимые методы могут принимать локаль явно. Т.е. например string.toLowerCase() == string.toLowerCase(Locale.getDefault())


Печаль.

·>Thread static — это вредительство какое-то в нашу эпоху асинков...


В нашу эпоху асинков thread static превратился в AsyncLocal и соотв. служебный код, обеспечивающий корректную инициализацию локалей.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.