Re: бессмысленные интерфейсы
От: Aquilaware  
Дата: 17.02.22 21:25
Оценка: +5 :)
Здравствуйте, Codealot, Вы писали:

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

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

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

Это становится похоже на поиск иголок в стоге сена — и по времени и по ментальным трудозатратам. Вот тогда иногда и делают отдельный интерфейс, который строго завляет — нужно только 2 таких вот метода.

А тысячи строк реализации — это уже дело второстепенное и совершенно неинтересное, особенно если над проектом работает несколько людей со своими зонами ответственности. Им нырять в чужие тысячи строк очень больно, но беглым взглядом увидеть суть в коротком определении интерфейса — очень легко и понятно.

Это классика подхода под названием "сервисо-ориентированая архитектура".
Отредактировано 17.02.2022 21:29 Aquilaware . Предыдущая версия . Еще …
Отредактировано 17.02.2022 21:27 Aquilaware . Предыдущая версия .
Отредактировано 17.02.2022 21:26 Aquilaware . Предыдущая версия .
Re[2]: бессмысленные интерфейсы
От: Codealot Земля  
Дата: 17.02.22 22:17
Оценка: -1 :)
Здравствуйте, Aquilaware, Вы писали:

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


Публичные методы — вверх. Всё.
И это все равно не тот случай.
Ад пуст, все бесы здесь.
Re[3]: бессмысленные интерфейсы
От: Тёмчик Австралия жж
Дата: 18.02.22 02:42
Оценка:
Здравствуйте, Codealot, Вы писали:

scf>>От языка зависит.


C>C#


Это многое обьясняет
Re: бессмысленные интерфейсы
От: Shmj Ниоткуда  
Дата: 18.02.22 09:54
Оценка:
Здравствуйте, Codealot, Вы писали:

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

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

Видимо дело вот в чем — люди любят те самые заголовочные файлы
Автор: Shmj
Дата: 10.02.22
— придумали аналог в виде контрактов/интерфейсов. Чтобы сразу было видно суть системы, даже если не для целей тестирования.
Re[3]: бессмысленные интерфейсы
От: B0FEE664  
Дата: 18.02.22 10:10
Оценка:
Здравствуйте, AlexGin, Вы писали:


AG>1) Если в качестве борьбы с перекрёстными ссылками?

AG>2) Для представления древовидных структур, с коллекциями элементов того же типа, что и корневой элемент?
AG>Когда в корневом элементе требуется иметь коллекцию child-элементов.
AG>как тогда быть?
AG>P.S. Подразумеваю C++ и наличие файлов *.h & *.cpp
Не вижу препятствий.

class child;

class parent
{
  std::vector<std::shared_ptr<child>> childs;
};

И каждый день — без права на ошибку...
Re[4]: бессмысленные интерфейсы
От: AlexGin Беларусь  
Дата: 18.02.22 12:02
Оценка:
Здравствуйте, уважаемый B0FEE664, Вы писали:

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

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

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


BFE>
BFE>class child;

BFE>class parent
BFE>{
BFE>  std::vector<std::shared_ptr<child>> childs;
BFE>};
BFE>


Элемент дерева, как логично я заметил выше, один и тот же.
Для каких-то других элементов — он является parent-ом.
Для других — чайлдом.
Ссылочку на этот чайлд хранит родитель (parent) в своей коллекции.

То, что Вы писали — это два разных класса.
Я бы предложил так:

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


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


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

Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее.
Кроме того, имеется заложенная база — как для развития (новые типы узлов),
так и для тестирования основы нашего проекта.
Отредактировано 19.02.2022 2:18 AlexGin . Предыдущая версия .
Re[3]: бессмысленные интерфейсы
От: AlexGin Беларусь  
Дата: 18.02.22 14:10
Оценка: +1
Здравствуйте, Codealot, Вы писали:

C>Публичные методы — вверх. Всё.

C>И это все равно не тот случай.

Различать всё-таки следует понятия: класс и модуль:

Класс — может выступать элементом внутренней структуры.
Его публичные методы — вполне возможно это внутренние шестерёнки,
оставленные открытыми только для работы с объектами этого класса его "начальниками" (клиентами).
Никто другой не работает с этим классом. Большинству окружающих даже не важно, что эти методы public.
В терминологии C++ есть понятие friend. Иногда я даже пользуюсь этим, нарушая принцыпы ООД.

То есть: публичные методы класса — не факт, что являются входами/выходами всго модуля (или даже подсистемы).

Чего уже НЕ СКАЖЕМ об интерфейсе: здесь методы именно определяющие взаимодействие модуля с внешним миром.
Они обязаны быть максимально независимыми от внутренней реализации. Простой "публичностью" здесь уже не обойтись.

P.S. Вот поэтому, реализация (даже если и единичная) нашего модуля ИМХО заслуживает наличия собственного интерфейса.
Это, казалось бы, усложнение — делает понимание кодов проекта проще и быстрее. Оно экономит силы и время (в том числе даже и
самому автору разработки, когда по воле случая он раскроет свои же коды 5-ти летней давности).
Отредактировано 18.02.2022 14:17 AlexGin . Предыдущая версия .
Re[4]: бессмысленные интерфейсы
От: Codealot Земля  
Дата: 18.02.22 15:01
Оценка: :)
Здравствуйте, AlexGin, Вы писали:

AG>Его публичные методы — вполне возможно это внутренние шестерёнки,


Значит, они не должны быть публичными. Всё.
Вот так, не умея использовать базовые конструкции языка, люди придумывают какую-то хрень.
Ад пуст, все бесы здесь.
Re[5]: бессмысленные интерфейсы
От: baxton_ulf США  
Дата: 18.02.22 19:45
Оценка: 3 (1) +1
Здравствуйте, AlexGin, Вы писали:

AG>Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее.

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

не просто "красивее", но и безопасней
1. контракт зафиксирован == клиенты могут начать разработку до того как будет закончена имплементация. клиенты могут писать юнит-тесты мокируя объект
2. имплементация спрятана от клиентов, что не позволяет особо продвинутым все сломать залазя туда своими кривыми руками
3. никто от балды не может добавить "полезный" метод в интерфейс
4. можно заменить реализацию и использовать две параллельно
5. интерфейс вообще можно отделить в отдельную либу и отправить на сторону клиента(например если мы пишем сетевые сервисы)
Re: бессмысленные интерфейсы
От: Codealot Земля  
Дата: 18.02.22 20:18
Оценка:
Поковырявшись еще в проекте, нашел немало перлов типа такого:

if (text.ToUpper().Contains(...))
    doSomething();
else if (text.ToUpper().Contains(...))
    doSomethingElse();
else if (text.ToUpper().Contains(...))
    doSomethingDifferentElse();


Так что вопрос о качестве кода можно закрывать. Джуниор, которого покусал Гамма.
Всех, пто придумывал оправдания такому коду — поздравляю.
Ад пуст, все бесы здесь.
Re[5]: бессмысленные интерфейсы
От: AlexGin Беларусь  
Дата: 18.02.22 21:05
Оценка: :)
Здравствуйте, Codealot, Вы писали:

AG>Его публичные методы — вполне возможно это внутренние шестерёнки,


C>Значит, они не должны быть публичными. Всё.


Да, именно, внутренние — для модуля.
Они могут выходить за пределы класса. Могут быть публичными для класса.

C>Вот так, не умея использовать базовые конструкции языка, люди придумывают какую-то хрень.


Вот так, люди не писавшие ничего сложнее "Hello world!", пытаются навязать своё видиние единственно правильной архитектуры ПО.
Re[6]: бессмысленные интерфейсы
От: Codealot Земля  
Дата: 18.02.22 21:29
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>Да, именно, внутренние — для модуля.


У тебя какое-то странное понимание модуля. Как и всего остального.

AG>Вот так, люди не писавшие ничего сложнее "Hello world!", пытаются навязать своё видиние единственно правильной архитектуры ПО.


Пальцем в небо. Очень сильно сомневаюсь, что у тебя есть хотя бы малейшие реальные основания для таких понтов.
Ад пуст, все бесы здесь.
Re[5]: бессмысленные интерфейсы
От: baxton_ulf США  
Дата: 18.02.22 21:52
Оценка: +1
Здравствуйте, Codealot, Вы писали:

Y>>Имелось в виду, что будут. Но потом. Но не срослось.


C>YAGNI. Если понадобится — тогда и надо добавлять.


потом не получится, правильно надо делать сразу.

пример, работал я над проектом в котором повсюду встречался код:
  ... // код какого-то метода
  someObj.setField(SomeConfig.getInstance().getFieldValue())
  ... // код продолжается


и все, пока в mockito не появился mockStatic никаких зубодробительных юнит-тестов не напишешь. а если это не java, то возможно вообще не напишешь

EDITED:
тоже относится к методам которые, например вычисляют даты и время у себя внутри. ты их не протестируешь потому как значения всегда будут разные
Отредактировано 18.02.2022 21:56 baxton_ulf . Предыдущая версия .
Re[6]: бессмысленные интерфейсы
От: Codealot Земля  
Дата: 18.02.22 22:19
Оценка:
Здравствуйте, baxton_ulf, Вы писали:

_>потом не получится


Ты не умеешь делать рефакторинг?

_>пример, работал я над проектом в котором повсюду встречался код:

_>
_>  ... // код какого-то метода
_>  someObj.setField(SomeConfig.getInstance().getFieldValue())
_>  ... // код продолжается
_>


В некоторых случаях это может быть оправдано. Лепить интерфейс для абсолютно каждого класса — идиотизм.
Ад пуст, все бесы здесь.
Re[5]: бессмысленные интерфейсы
От: · Великобритания  
Дата: 18.02.22 23:17
Оценка: +2
Здравствуйте, AlexGin, Вы писали:

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

ну никто не мешает сделать одну:
class node
{
  std::vector<std::shared_ptr<node>> childs;
};


AG>Но у меня — универсальный узел и интерфейс к нему.

Нет, у тебя узел и иузел.

AG>Именно наличие программного интерфейса — позволяет уменьшить связанность (coupling) в проекте.

А конкретно в данном случае что именно уменьшается?

AG>За счёт этого, получим упрощение архитектуры и уменьшение количества ошибок.

Введение новой сущности интерфейса не упрощает, а усложняет.

AG>Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее.

Эмоции. "Я так всегда делал и мне нравится"

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

yagni. Вот _если_ появятся новые узлы — вот тогда и добавим интерфейс. Впрочем, тут тоже могу условно согласиться. Если средство разработки не умеет в рефакторинг "выделить интерфейс", то приходится прыгать... впрочем и без авто-тулзов ввести интерфейс _при необходимости_ относительно просто. Проще, чем постоянно копаться в сотнях интерфейсов, созданных на всякий случай.

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

Вот тут могу согласиться для некоторых ЯП. Если ЯП не позволяет тестировать без интерфейсов, то их приходится вводить на каждый чих.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: бессмысленные интерфейсы
От: · Великобритания  
Дата: 18.02.22 23:20
Оценка:
Здравствуйте, AlexGin, Вы писали:

AG>Чего уже НЕ СКАЖЕМ об интерфейсе: здесь методы именно определяющие взаимодействие модуля с внешним миром.

Это прост какие-то соглашения. Раз интерфейс, давайте его считать чем-то особым и назовём "интерфейсом модуля". Врочем, это опять всё от конкретного ЯП зависит. В плюсах там с модульностью вообще плохо. Вот и приходится опираться на определённые соглашения.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[6]: бессмысленные интерфейсы
От: AlexGin Беларусь  
Дата: 19.02.22 03:01
Оценка:
Здравствуйте, ·, Вы писали:

AG>>Но у меня — универсальный узел и интерфейс к нему.

·>Нет, у тебя узел и иузел.

Напомню, что в терминологии C++ (в отличие от C# и Java) понятие интерфейс отсутствует.
Вместо него обычно применяется: абстрактный_класс:
https://www.tutorialspoint.com/cplusplus/cpp_interfaces.htm

AG>>Именно наличие программного интерфейса — позволяет уменьшить связанность (coupling) в проекте.

·>А конкретно в данном случае что именно уменьшается?

Показана идея применения абстрактного_класса C++ в качестве интерфейса.
Здесь (в приведенном примере) всё просто, поэтому избыточной связанности нет.

AG>>За счёт этого, получим упрощение архитектуры и уменьшение количества ошибок.

·>Введение новой сущности интерфейса не упрощает, а усложняет.

Конкретно здесь — упрощает понимание (даже если незначительно и увеличивает объём кода).

AG>>Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее.

·>Эмоции. "Я так всегда делал и мне нравится"

Если быть точным, то делал я так не всегда.
Первые лет пять/шесть разработки на C++ я практически не применял абстрактные классы.
Но примерно с 2015-го понял, что такой подход упрощает понимание проекта.
Даже мне самому легче понять то, что я разработал несколько лет назад.
В литературе это именуют: best practices.

P.S. Мы не ждём, когда спроектируются новые узлы поезда/вагоны, а подготавливаем проект "рельсового пути" для существующих и новых поездов.
Вы же предлагаете проектировать новые рельсы, как только появятся новые вагоны.
YAGNI — если бы я предлагал проектировать поезд на магнитной подушке, для поездки по линии длиной 5 км, где вполне хватит обычного трамвая.
Отредактировано 19.02.2022 3:09 AlexGin . Предыдущая версия . Еще …
Отредактировано 19.02.2022 3:03 AlexGin . Предыдущая версия .
Re[6]: бессмысленные интерфейсы
От: elmal  
Дата: 19.02.22 06:59
Оценка: +1
Здравствуйте, baxton_ulf, Вы писали:

_>пример, работал я над проектом в котором повсюду встречался код:

_>
_>  ... // код какого-то метода
_>  someObj.setField(SomeConfig.getInstance().getFieldValue())
_>  ... // код продолжается
_>


_>и все, пока в mockito не появился mockStatic никаких зубодробительных юнит-тестов не напишешь. а если это не java, то возможно вообще не напишешь

Элементарно рефакторится.

Начнем с того, что SomeConfig.getInstance().getFieldValue() — это лютый копипаст и так делать нельзя и от такого нужно избавляться мгновенно на автомате как только увидел такое.


Ну и переписывается это на что то вроде
someObj.setField(ConfigHelper.getFieldValue())
,
причем можно еще и static import сделать для читаемости.

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

И все — все дополнительные сложности локализованы в одном месте, очень быстрым легким и безопасным рефакторингом, весь остальной код именно что жестко перелопачивать не требуется. Уже утилитные Helper классы пишутся с нуля, там могут быть любые навороты.

А далее уже можно подумать над использованием фреймворков, где уже есть всякие DI в зависимости от контекста, где уже задача нормального тестирования, мокирования, подмены конфигов и т.д работает из коробки и т.д. Если основной код требуется сделать независим от таких фреймворков — ок, хорошо, просто используем такие фреймворки только в тестах, делая мостик между тем, как положено там тестировать и нашими костылями с подменой конфига.
Re[7]: бессмысленные интерфейсы
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.02.22 07:21
Оценка:
Здравствуйте, elmal, Вы писали:

E>Начнем с того, что SomeConfig.getInstance().getFieldValue() — это лютый копипаст и так делать нельзя и от такого нужно избавляться мгновенно на автомате как только увидел такое.

E>Ну и переписывается это на что то вроде
E>
E>someObj.setField(ConfigHelper.getFieldValue())
E>
,

E>причем можно еще и static import сделать для читаемости.
E>ConfigHelper уже пишется таким образом, что уже достаточно легко можно определять с каким конфигом работать, тестовым или нет, причем переопределять все в рантайме. Непосредственно в тестах быстро пишется утилита, которая позволяет работать с разными конфигами.

А что, getInstance() не даст переопределить конфиг на любой тестовый?
Кроме чуть более длинной записи (и то можно результат getInstance() закэшировать в пределах метода) — проблемы не видно.
А делать на каждое поле отдельный метод ещё и в ConfigHelper — задолбёшься.
Хотя если это ConfigHelper().getFooValue() — то ещё более-менее норм.

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


Ну так подменять объект конфигурации для теста — очевидность.
The God is real, unless declared integer.
Re[2]: бессмысленные интерфейсы
От: elmal  
Дата: 19.02.22 07:27
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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

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