Re: Интерфейсы и реализация
От: · Великобритания  
Дата: 01.07.20 22:12
Оценка: +4
Здравствуйте, Буравчик, Вы писали:

Б>Чем руководствуетесь, принимая решение выделять или не выделять интерфейс из класса, т.е. нужно ли разделить интерфейс и реализацию?

KISS же. Язык Java. Интерфейс создаю только когда без него никак. IDEA умеет рефакторинг "выделить интерфейс", так что нет никакой проблемы добавить когда реально понадобится.
Тест-фреймворк позволяет мокать любой класс.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Интерфейсы и реализация
От: СвободуАнжелеДевис СССР  
Дата: 01.07.20 21:49
Оценка: +1 -2
Б>Когда имеется несколько реализация — понятно, интерфейс обычно выделяют.
Б>Но если реализация только одна, для каких классов выделяете интерфейсы, а для каких нет?

Выделяется при любом взаимодействии двух и более классов, ибо тесты.

Б>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.


С#
Нет времени на раскачку!
Re[8]: Интерфейсы и реализация
От: · Великобритания  
Дата: 27.07.20 11:56
Оценка: +3
Здравствуйте, a7d3, Вы писали:

A>>>·>Во-первых, в С++ нет интерфейсов. Так что не очень понятно что конкретно ты имеешь в виду.

A>>>В каком плане в С++ нет интерфейсов?
A>·>В языке такого понятия нет.
A>Всегда было и вдруг исчезло? https://en.cppreference.com/w/cpp/language/abstract_class
Абстрактный класс это не то же самое, что интерфейс. Врочем, абстрактные классы для DI тоже не нужны.

A>>>И что именно понимается под интерфейсами, которые якобы не нужны в DI?

A>·>Ищем в гугле "di c# example", первая ссылка вот. Там interface ICustomerDataAccess не нужен, и даже немножко вреден.
A>Чем именно вреден и
Насчёт "не нужен" возражений нет?
Вреден как минимум тем, что усложняет пример на пустом месте и учит новичков переусложнизму.

A>в каком из сценариев использования

Как минимум в упомянутом по ссылке выше.

A> DI (IoC)?

Ещё с терминологией надо бы разобраться. IoC — это принцип инверсии контроля. Применяя этот принцип к управлению зависимостями получаем технику Dependency Injection. И не путай IoC principle и IoC-container.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[11]: Интерфейсы и реализация
От: a7d3  
Дата: 27.07.20 20:44
Оценка: -3
Здравствуйте, ·, Вы писали:

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


A>>>>Всегда было и вдруг исчезло? https://en.cppreference.com/w/cpp/language/abstract_class

A>>·>Абстрактный класс это не то же самое, что интерфейс. Врочем, абстрактные классы для DI тоже не нужны.
A>>Спорное утверждение,
·>Которое из моих утверждений спорное?

A>> таковы средства данного языка для реализации такой концепции, как интерфейсы.

·>Эээ. И что? Средствами даже голого C можно реализовать, например, такую концепцию как рефлексия. И что? Она внезапно там появится?

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

A>>Рассмотрим и тот, и этот варианты, положив на весы в плоскости конкретных, однозначно сформулированных целей и задач.

A>>После чего станет ясно, в каком аспекте, в каком разрезе проигрывает или хорош тот или иной вариант.
A>>Не проблема?
·>Я не понял вопрос. Тебе непонятна альтернатива что-ли? Я же уже написал — просто выкинуть interface ICustomerDataAccess и собственно всё. По-моему очевидно что должно получиться. Код что-ли привести?

Конкретика должна быть такой:
1) для чего именно было решено использовать DI, с какой целью, ради каких задач.
2) какой вариант, в контексте п.1, более вменяем, нежели через интерфейсы.
Re[3]: Интерфейсы и реализация
От: · Великобритания  
Дата: 02.07.20 15:17
Оценка: 12 (2)
Здравствуйте, Буравчик, Вы писали:

Б>·>KISS же. Язык Java. Интерфейс создаю только когда без него никак. IDEA умеет рефакторинг "выделить интерфейс", так что нет никакой проблемы добавить когда реально понадобится.

Б>В каких случаях добавляешь интерфейсы? Как отличить ситуацию, когда "без интерфейса никак" и когда "без интерфейса норм"?
В подавляющем большинстве случаев это когда более одной реализации.
Ещё иногда для выделения API между разными модулями (но это в каком-то смысле то же самое). Например, можно начать писать код, выделяя что-то в интерфейсы, мОкая их в тестах. А потом интерфейсы реализуется кем-то другим.
Другие случаи что-то в голову сейчас не приходят, вспомню — напишу.

Публичные методы класса это и есть интерфейс по сути.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 02.07.2020 15:19 · . Предыдущая версия .
Re[10]: Интерфейсы и реализация
От: · Великобритания  
Дата: 27.07.20 19:14
Оценка: +2
Здравствуйте, a7d3, Вы писали:

A>>>Всегда было и вдруг исчезло? https://en.cppreference.com/w/cpp/language/abstract_class

A>·>Абстрактный класс это не то же самое, что интерфейс. Врочем, абстрактные классы для DI тоже не нужны.
A>Спорное утверждение,
Которое из моих утверждений спорное?

A> таковы средства данного языка для реализации такой концепции, как интерфейсы.

Эээ. И что? Средствами даже голого C можно реализовать, например, такую концепцию как рефлексия. И что? Она внезапно там появится?

A>>>Чем именно вреден и

A>·>Насчёт "не нужен" возражений нет?
A>·>Вреден как минимум тем, что усложняет пример на пустом месте и учит новичков переусложнизму.
A>В том примере интерфейсы используются для конкретного назначения, если утверждается, что можно проще то, что именно предлагается в качестве альтернативы?
Тот пример вроде бы как должен демонстрировать применение DI. И примеры, как правило, должны быть минимальны. Использование интерфейса там как пятая нога.

A>Рассмотрим и тот, и этот варианты, положив на весы в плоскости конкретных, однозначно сформулированных целей и задач.

A>После чего станет ясно, в каком аспекте, в каком разрезе проигрывает или хорош тот или иной вариант.
A>Не проблема?
Я не понял вопрос. Тебе непонятна альтернатива что-ли? Я же уже написал — просто выкинуть interface ICustomerDataAccess и собственно всё. По-моему очевидно что должно получиться. Код что-ли привести?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[6]: Интерфейсы и реализация
От: samius Япония http://sams-tricks.blogspot.com
Дата: 03.09.20 15:52
Оценка: +1 :)
Здравствуйте, rosencrantz, Вы писали:

R>Тс задал вопрос про классы, поэтому я отвечаю про классы. Нужен интерфейс или не нужен (у меня) диктуется тем, хотим мы выделить модуль или нет.




Видимо, предлагается проголосовать?
Re[16]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 24.10.20 13:34
Оценка: 80 (1)
Здравствуйте, Sharov, Вы писали:

S>Подождите, мы DI рассматриваем в контексте IoC контейнеров, где мы в явном виде new никогда не вызываем, а используем,

S>например, абстрактную фабрику. Ну так вот IoC у меня созвучен с dip, в том плане, что это dip для контроля управления.
S>А dip говорит, что завязываться на детали (реализации) плохо, лучше завязываться на абстракции. Т.е. мы куда-то встраиваемся
S>(во фреймворк) через интерфейсы и абстрактные классы.

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

Раз, для тебя близок ASP.NET Core, — то далеко ходить и не будем. Можно взять его ILogger, как пример. К нему, как к инфраструктурному компоненту должны предъявляться повышенные требования, а на деле — он сам себя закрывает за ущербным интерфейсом из 3 методов, при чём поведение третьего метода — не гарантированно (scoped logging). Реально используемые методы (типа LogDebug) — выполнены вообще в виде экстеншнов, хотя здесь практически нет места свободному расширению, как в LINQ, которые в итоге форсируют использовать using namespace, даже если у тебя доступ к логгеру уже есть.

Ну и вишенка на торте, что практически у любого логгера первое, что он делает, это условие на подобии такого:

  if (logEventLevel < _currentMinimumLevel) return;
  // slow path


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

Ровно как и здоровые фреймворки, никогда не будут делать так:
public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args)


Предпочитая следующие вызовы:
public void LogTrace<T1>(Exception exception, string message, T1 arg1);
public void LogTrace<T1, T2>(Exception exception, string message, T1 arg1, T2 arg2);
public void LogTrace<T1, T2, T3>(Exception exception, string message, T1 arg1, T2 arg2, T3 arg3);


Да, да, всё ради того же: избавиться от холостых выделений памяти, да и generic инвокация окажется короче. Но, опять же, это невозможно, т.к. мы ограничены ущербным интерфейсом.

Это не особо важно для уровней Info и выше, т.к. они практически всегда логгируются, и механика материализации съест немало, но уровни Trace и Debug — в нормальном режиме практически всегда подавлены и достаточно чувствительны к этому. (И не надо петь про дешевость вызовов — любой, даже безусловный переход (jmp) съедает слот в предсказателе переходов.)

Ну и с практической точки зрения, для пользователя, абсолютно фиолетово, что использовать ILogger, Logger, тем более в контейнере можно ещё зарегистрировать и NLog.Logger и Serilog и всё это будет вместе жить через адаптеры, при чём бэкэндом будет любой из них. В конце концов Microsoft.Extensions.Logging — по отношению к твоему коду — просто ещё одна бибилиотека со своим интерфейсом, и фактически — со своим поведением и с единственной реализацией.

Там есть и другие нюансы у них, но по сути, я вел не к тому, какое плохое или хорошее у них логгирование (оно вполне достаточное и фреймворк достаточно универсальный... что бы его не использовать), а просто пример, того, как легко запереть себя в "абстракциях" отрезав любые эффективные решения как класс. В данном случае — абстракция (в виде интерфейса) — вообще не нужна.
Отредактировано 24.10.2020 13:44 Mystic Artifact . Предыдущая версия . Еще …
Отредактировано 24.10.2020 13:42 Mystic Artifact . Предыдущая версия .
Отредактировано 24.10.2020 13:35 Mystic Artifact . Предыдущая версия .
Re[12]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 27.07.20 21:32
Оценка: 1 (1)
Здравствуйте, a7d3, Вы писали:

A>Конкретика должна быть такой:

A>1) для чего именно было решено использовать DI, с какой целью, ради каких задач.
A>2) какой вариант, в контексте п.1, более вменяем, нежели через интерфейсы.

DI ортогонален IoC, контейнерам и интерфейсам. Есть, и их масса, классов которые потребляют иные объекты через DI. Чего тут аргументировать то?

Все вывороты с интерфейсам и абстрактными классами имеют смысл только в случае, когда в рантайме мы можем предоставить разные имплементации одного и того же нечто (сервиса).

Всё о чем тут говорят — что на практике — интерфейсы плодятся без всякого повода, просто, потому, что это модно и примеры лишь подталкивают к этому. 99.9% приложений имеют единственный конкретный SQL-бэкэнд, и абстрагироваться не только нет смысла, но даже и вредно, т.к. замоченные фейки и близко не несут реальной семантики и реальных возможных проблем.

Вы спорите о чем угодно, но не о теме.
Re[2]: Интерфейсы и реализация
От: · Великобритания  
Дата: 02.09.20 20:11
Оценка: 1 (1)
Здравствуйте, rosencrantz, Вы писали:

R>Если интерфейс выкинуть и оставить только сам BatchInsertingUserHandler, тут возникает много неясностей

Я не вижу никаких неясностей. Что плохого с именем "UserHandler" для этого класса? Зачем этот префикс BatchInserting?

class UserCsvReader {
  void readAllUsers(UserHandler userHandler) {
    ...
  }
}

class BatchInserter<TRow> {
  BatchInserter(String sql, int batchSize) {...}

  void insert(TRow row) {
    if (batch.size < batchSize) {
      batch.add(row);
    } else {
      connection.batchUpdate(sql, batch);
      batch.clear();
    }    
  }
}

class UserHandler {
  UserHandler(BatchInserter<UserRow> batchInserter) {}

  void handleUser(User user) {
    UserRow r = makeRowFromUser(user);
    batchInserter.insert(r);
  }
}

BatchInserter<UserRow> batchInserter = new BatchInserter("insert into Users(...)");
UserHandler userHandler = new UserHandler(batchInserter);
UserCsvReader reader = new UsersCsvReader();
reader.readAllUsers(userHandler);

И что не так?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 02.09.2020 20:18 · . Предыдущая версия .
Re: Интерфейсы и реализация
От: scf  
Дата: 26.07.20 08:28
Оценка: +1
Здравствуйте, Буравчик, Вы писали:

Б>Чем руководствуетесь, принимая решение выделять или не выделять интерфейс из класса, т.е. нужно ли разделить интерфейс и реализацию?


Java/Scala
Еще две причины:
— Тестирование. тестовую реализацию интерфейса можно заимплементить, не сваязываясь с фреймворками для моков.
— Оформление кода. отдельный интерфейс с джавадоком — это удобная документация для программиста, не загроможденная реализацией.
Re[7]: Интерфейсы и реализация
От: a7d3  
Дата: 26.07.20 22:57
Оценка: +1
Здравствуйте, ·, Вы писали:

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


A>>>>·>А зачем для DI интерфейсы? Это специфика C# что-ли?

A>>>>Хотелось бы посмотреть на DI без интерфейсов в случае юнит-тестов на С++.
A>>·>Во-первых, в С++ нет интерфейсов. Так что не очень понятно что конкретно ты имеешь в виду.
A>>В каком плане в С++ нет интерфейсов?
·>В языке такого понятия нет.

Всегда было и вдруг исчезло? https://en.cppreference.com/w/cpp/language/abstract_class

A>>И что именно понимается под интерфейсами, которые якобы не нужны в DI?

·>Ищем в гугле "di c# example", первая ссылка вот. Там interface ICustomerDataAccess не нужен, и даже немножко вреден.

Чем именно вреден и в каком из сценариев использования DI (IoC)?
Re[3]: Интерфейсы и реализация
От: scf  
Дата: 28.07.20 06:14
Оценка: +1
Здравствуйте, Mystic Artifact, Вы писали:

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


scf>>- Оформление кода. отдельный интерфейс с джавадоком — это удобная документация для программиста, не загроможденная реализацией.


MA> Получается я не один такой
Автор: Mystic Artifact
Дата: 26.07.20
? Насколько считаешь этот момент важным?

MA> Я довольно часто мечусь по неизвестному коду (даже по своему...), и если это не C++ честно говоря порой офигеваю...

Нууу, почти. На мой вкус, у хедеров С++ есть два недостатка. Во-первых, они обязательны. Т.е. даже если класс или модуль очень простой или не очень важный, их все равно приходится писать. Во-вторых, хедеры всегда в отдельном файле, удваивая кол-во файлов в проекте.
Re[14]: Интерфейсы и реализация
От: · Великобритания  
Дата: 28.07.20 15:20
Оценка: +1
Здравствуйте, a7d3, Вы писали:

A>>>1) для чего именно было решено использовать DI, с какой целью, ради каких задач.

A>·>Там по ссылке расписано. Если чего неясно, задавай вопросы.
A>Интересует конкретно твой кейс или множество таковых, DI не может быть самоцелью.
Да причём тут я? Я вообще не рассматривал никакие конкретные кейсы. Я лишь выдвинул тезис, что интерфейсы|абстактные классы к DI никакого отношения не имеют, и чаще всего не нужны.
Если тебя интересуют конкретные кейсы, давай приводи конкретный пример, обсудим.

A>>>2) какой вариант, в контексте п.1, более вменяем, нежели через интерфейсы.

A>·>Третий раз отвечаю — всё то же самое, но без интерфесов. Просто инжектить конкретный класс.
A>В четвёртый раз DI не самоцель, конкретные цели с задачами «в студию!» и только после этого обсуждать уже аспекты конкретных подходов.
В данном контексте цель была продемонстрировать DI. И тот туториал по ссылке (и подавляющиее число других) это делал коряво, усложнив код введением лишней сущности "интерфейс" — просто так, потому что "так принято", "хороший тон", никакого рационального обоснования там нет, и ты не привёл. Я не понимаю что ты хочешь доказать. Сформулируй свой тезис.

A>Например, такой подход, когда в случае C# инжектирую конкретные классы ты будешь использовать System.Object в неявной форме, забыв что там под капотом в плане интерфейсов и занимаясь приведением типов к тому «контракту», который ожидается в жертве инжекта (клиенте).

Не понял мысль. Можно пример кода?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[12]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 29.07.20 20:49
Оценка: -1
Здравствуйте, Sharov, Вы писали:

S>Интерфейс нужен с прицелом на юнит тестирование.

Для нормального тестирования нет такой необходимости в принципе. Ты своим утверждением просто на корню отрезаешь огромный класс тестов который чаще всего именуем как black-box тестирование. А в black-box на самом деле можно включить от "поведенческих" тестов, до интеграционных. Интеграционные ближе всех похожи на black-box, но и подсовывание фейков (а еще хуже рантайм моков) — в некотором смысле само по себе интеграционное тестирование (компонентнов).
По сути, что я хочу донести — интерфейсы никак не облегчают тестирование и не нужны для этого вообще, если вы не собрались тестировать насколько качественно компилятор успешно компилирует условные выражения.
Компиляторы сами по себе — отличнейший пример где типичное black-box тестирование работает естественным образом.
Классы достаточно часто содержат приватные методы которые нуждаются но до них невозможно добраться. И да — вместо того, что бы сделать ровно то, что нужно, чаще всего нам советуют ахинею.
Для преодоления этих ограничений, часто появляются методы навроде DoSomethingForTests. Существование подобных методов всегда говорит ровно о 2-3 вещах: 1) публичный API ущербен 2) современные языки ущербны — они не дают возможности работать со СВОИМ кодом, как того требует ситуация 3) как следствие это выражается в костылях: в C# — internal + комментарий о том что это только для тестов (учти — этот метод — констпуктор, и никаких суффиксов не имеет), а комментарий — компилятор нагло игнорирует.
Я все эти техники видел на протяжении всей жизни, они актуальны и сейчас: это говно широко распространено как в .net, абсолютно такая же ситуация в могучем C++.
Ну, и разумеется здесь есть импликация: раз вы (обобщенно) не умеете тестировать классы без зависимостей — то о чем дальше вообще можно вести речь? А вы не умеете — свществующие языки скудоумны и не позволяют. Ввести скопы видтмости это не по феншую а делать паблики *ForTests или internal + InternalVisibleTo это конечно же лучше.
Это что ни наесть — натуральный идиотизм. Никто и ничем не защитится от грубой силы, те же фреймворки навроде Fakes это отлично демонстрируют.
Именно поэтому я и говорю — интерфейс не нужен доя юнит тестирования. Возможность подменить зависимость, лишь дает иллюзию контроллируемости процесса, но на самом деле, (ну оооочень утрированно) вместо нее класс тихо потребляет статическую зависимость и делает вид что все равно работает.

А если кратко: фокусироваться нужно или на поведении или на реализации. Тестирование реализации может (и должно) потребовать доступ к инвариантам или именно импоемениации где ессно лучшие пёрлы скрыты за private. Либо фокусироваться именно на публичном контракте — но это тоже самое что black-box. Любые тесты кто внедряет фейковые зависимости — фактически неявно скрывают за собою закодированное! знание об устройстве мира. Либо тривиальеы и тестируют оператор if.

Со мной в целом то не надо соглашаться, но интерыейс ради теста... это то чего следует избегать, если это возможно в рамках разумного.
Re[4]: Интерфейсы и реализация
От: diez_p  
Дата: 24.10.20 03:45
Оценка: +1
Здравствуйте, Sharov, Вы писали:

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


S>>>.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.

_>>А что вам мешает подсоввать тип напрямую?

S>А зачем тогда DI? А если я конфигурирую реализации в конфиге, и тип узнаю только в runtime?

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

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

Условно есть три сервиса
S1, S2, S3 — конкретные типы.

Есть клиенты этих сервисов
C12(S1, S2), C23(S2, S3), C123(S1, S2, S3)
регистрируется все это
Di.Register<S1>
Di.Register<S2>
Di.Register<S3>
Di.Register<C12>
Di.Register<C23>
Di.Register<C123>

при вызове DI.Resolve<C123> контейнер создаст S1, S2, S3, а при вызове DI.Resolve<C12>, только S1, S2
На кой черт при таком дизайне раздувать контракт интерфейсами — не вижу смысла, т.е. сам по себе DI выделения интерфейса не требует.
Re[6]: Интерфейсы и реализация
От: · Великобритания  
Дата: 24.10.20 07:45
Оценка: +1
Здравствуйте, scf, Вы писали:

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

scf>Две причины.
Обсудили же это уже. Плохие это причины.

scf>Интерфейс служит документацией к классу, не загромождая её реализацией,

Такая документация элементарно извлекается автоматически. Писать и поддерживать её вручную — так себе развлечение.

scf>интерфейс позволяет подсунуть другую реализацию в юнит-тестах.

Это проблема конкретных мок-фреймворков. Вменяемые фреймворки умеют мокать публичные методы класса.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[16]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 24.10.20 08:44
Оценка: +1
Здравствуйте, Sharov, Вы писали:

S>Подождите, мы DI рассматриваем в контексте IoC контейнеров, где мы в явном виде new никогда не вызываем, а используем,

S>например, абстрактную фабрику. Ну так вот IoC у меня созвучен с dip, в том плане, что это dip для контроля управления.
S>А dip говорит, что завязываться на детали (реализации) плохо, лучше завязываться на абстракции. Т.е. мы куда-то встраиваемся
S>(во фреймворк) через интерфейсы и абстрактные классы.

DIP во многом зависит и от ЯП, и тут есть некоторые нюансы, и Мартин об этом писал, кстати, что в C++ хидер-файл включает все детали класса, включая все его приватные методы или поля, и даже инклюдит соответственно то, что ему нужно для приватных членов, и "настоящее" разделение там проще сделать через наследование от чистого абстрактного класса (считай это интерфейс в Java/C#).

В Java/C# — напротив — мы имеем автоматическое отделение интерфейса от реализации, в добавок с символическим разрешением зависимостей в рантайме. Ты можешь положить в сборку MyClass1 и будешь видеть исключительно его публичный контракт (публичный интерфейс), без каких либо дополнительных телодвижений, а его изменение лайаута — тебя вообще не волнует, рантайм позабоится об этом сам. И таким образом MyClass1 для всех потребителей — и есть уже эта самая абстракция, интерфейс.

Введение же интерфейсов или абстрактных классов в терминах C#/Java — должны уже иметь на это основания: точки расширения в фреймворках, множество реализаций и т.п.
Re[17]: Интерфейсы и реализация
От: · Великобритания  
Дата: 24.10.20 18:52
Оценка: +1
Здравствуйте, Mystic Artifact, Вы писали:

S>>Подождите, мы DI рассматриваем в контексте IoC контейнеров, где мы в явном виде new никогда не вызываем, а используем,

S>>например, абстрактную фабрику. Ну так вот IoC у меня созвучен с dip, в том плане, что это dip для контроля управления.
S>>А dip говорит, что завязываться на детали (реализации) плохо, лучше завязываться на абстракции. Т.е. мы куда-то встраиваемся
S>>(во фреймворк) через интерфейсы и абстрактные классы.
MA> DIP во многом зависит и от ЯП, и тут есть некоторые нюансы, и Мартин об этом писал, кстати, что в C++ хидер-файл включает все детали класса, включая все его приватные методы или поля, и даже инклюдит соответственно то, что ему нужно для приватных членов, и "настоящее" разделение там проще сделать через наследование от чистого абстрактного класса (считай это интерфейс в Java/C#).
Согласен. Стоит отметить, что это тоже довольно условно относится к интерфейсам. В плюсах разделять можно например через CRTP или даже pImpl, без всякого наследования и интерфейсов. И уж тем более это всё ортогонально к DI.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Интерфейсы и реализация
От: Буравчик Россия  
Дата: 01.07.20 21:34
Оценка:
Чем руководствуетесь, принимая решение выделять или не выделять интерфейс из класса, т.е. нужно ли разделить интерфейс и реализацию?

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

P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.
Best regards, Буравчик
Re: Интерфейсы и реализация
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 02.07.20 02:09
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Чем руководствуетесь, принимая решение выделять или не выделять интерфейс из класса, т.е. нужно ли разделить интерфейс и реализацию?


Б>Когда имеется несколько реализация — понятно, интерфейс обычно выделяют.

Б>Но если реализация только одна, для каких классов выделяете интерфейсы, а для каких нет?

Б>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.


От языка сильно зависит. В том же C++ не выделяю интерфейсы до тех пор, пока без этого никак нельзя, т.е. крайне редко, так как довольно часто можно обойтись статическим полиморфизмом вместо динамического.

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

В Go ситуация диаметрально противоположная, там сам язык тебя подталкивает к тому, что бы как можно чаще выделять минимальные интерфейсы. Так что на Go интерфейсов всегда много и они выполняют строго одну задачу.
Re: Интерфейсы и реализация
От: Homunculus Россия  
Дата: 02.07.20 02:18
Оценка:
Здравствуйте, Буравчик, Вы писали:

C++
Я стараюсь писать классы так, чтоб H и CPP файл можно было просто взять и воткнуть в любой другой проект. Конечно, не всегда это возможно, но стараюсь.

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

Так же не обойтись без интерфейсов в плагинной системе.

Так же делегаты и коллбеки оборачиваю в интерфейсы.
Re: ВСЕГДА
От: LaptevVV Россия  
Дата: 02.07.20 04:31
Оценка:
Ибо тестирование, развитие программы
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: Интерфейсы и реализация
От: Sharov Россия  
Дата: 02.07.20 10:24
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Чем руководствуетесь, принимая решение выделять или не выделять интерфейс из класса, т.е. нужно ли разделить интерфейс и реализацию?

Б>Когда имеется несколько реализация — понятно, интерфейс обычно выделяют.
Б>Но если реализация только одна, для каких классов выделяете интерфейсы, а для каких нет?
Б>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.

.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.
Кодом людям нужно помогать!
Re[2]: Интерфейсы и реализация
От: Буравчик Россия  
Дата: 02.07.20 10:46
Оценка:
Здравствуйте, kaa.python, Вы писали:

KP>В случае с Python и того реже есть такая необходимость, благодаря динамической типизации, хотя если хочется точно указать какие методы должны быть реализованы, то abc наш лучший друг и соратник.


Я пишу на python. Тестировать можно и без интерфейсов, но руки все равно тянутся выделять интерфейсы, чтобы явно выделить контракт сервиса и развязать компоненты между собой (DI). И каждый раз, когда я создаю интерфейс, меня терзают мысли об overdesign Ведь это совсем не python-way
Best regards, Буравчик
Re[2]: Интерфейсы и реализация
От: Буравчик Россия  
Дата: 02.07.20 10:51
Оценка:
Здравствуйте, ·, Вы писали:

·>KISS же. Язык Java. Интерфейс создаю только когда без него никак. IDEA умеет рефакторинг "выделить интерфейс", так что нет никакой проблемы добавить когда реально понадобится.


В каких случаях добавляешь интерфейсы? Как отличить ситуацию, когда "без интерфейса никак" и когда "без интерфейса норм"?
Best regards, Буравчик
Re[2]: Интерфейсы и реализация
От: · Великобритания  
Дата: 02.07.20 15:02
Оценка:
Здравствуйте, Sharov, Вы писали:

Б>>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.

S>.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.
А зачем для DI интерфейсы? Это специфика C# что-ли?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: Интерфейсы и реализация
От: Sharov Россия  
Дата: 02.07.20 19:35
Оценка:
Здравствуйте, ·, Вы писали:

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


Б>>>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.

S>>.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.
·>А зачем для DI интерфейсы? Это специфика C# что-ли?

1)необязательно, типа признака хорошего тона. Еще и буковка D в SOLID.
2)как иначе изменить реализацию через какой-нибудь конфиг файл?
Кодом людям нужно помогать!
Отредактировано 02.07.2020 19:37 Sharov . Предыдущая версия .
Re[4]: Интерфейсы и реализация
От: · Великобритания  
Дата: 02.07.20 21:05
Оценка:
Здравствуйте, Sharov, Вы писали:

S>>>.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.

S>·>А зачем для DI интерфейсы? Это специфика C# что-ли?
S>1)необязательно, типа признака хорошего тона.
Культ Карго, иначе говоря.

S>Еще и буковка D в SOLID.

KISS über allen. Если классы в разных модулях это ещё имеет смысл. Но когда у тебя каждый класс продублирован интерфейсом, то просто мусорный код, который только мешается.

S>2)как иначе изменить реализацию через какой-нибудь конфиг файл?

Если реализацию можно изменить, значит у тебя несколько реализаций. Этот случай, вроде, не рассматриваем. Тут, ясен пень, интерфейс нужен.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 02.07.2020 21:06 · . Предыдущая версия .
Re[3]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 26.07.20 01:57
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Я пишу на python. Тестировать можно и без интерфейсов, но руки все равно тянутся выделять интерфейсы, чтобы явно выделить контракт сервиса и развязать компоненты между собой (DI). И каждый раз, когда я создаю интерфейс, меня терзают мысли об overdesign Ведь это совсем не python-way


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

Используя C# хочется иногда вводить подобные интерфейсы, но приходится себя останавливать, т.к. в нем это инструмент для разрешения иных задач. Поэтому без необходимости не ввожу. Например, если, я сделаю struct (value-type) которая реализует интерфейс — это с одной удовлетворит мои желания, а с другой может провоцировать пользователей передавать их именно через интерфейс (что приведет к боксингу) — что аннигилирует саму цель её существования. Ну, в более стандартных ситуациях это все равно выливается в лишний шум.
Re[3]: Интерфейсы и реализация
От: a7d3  
Дата: 26.07.20 09:05
Оценка:
Здравствуйте, ·, Вы писали:

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


Б>>>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.

S>>.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.
·>А зачем для DI интерфейсы? Это специфика C# что-ли?

Хотелось бы посмотреть на DI без интерфейсов в случае юнит-тестов на С++.
Re[4]: Интерфейсы и реализация
От: · Великобритания  
Дата: 26.07.20 18:56
Оценка:
Здравствуйте, a7d3, Вы писали:

Б>>>>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.

S>>>.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.
A>·>А зачем для DI интерфейсы? Это специфика C# что-ли?
A>Хотелось бы посмотреть на DI без интерфейсов в случае юнит-тестов на С++.
Во-первых, в С++ нет интерфейсов. Так что не очень понятно что конкретно ты имеешь в виду.
Во-вторых, класс с вирт-функциями мокается ровно же как и в Java/C#, без всяких интерфейсов. Или ты конструктор имеешь в виду?
В-третьих, другие опции есть, шаблоны там, препроцессор, подмена .cpp системой сборки.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: Интерфейсы и реализация
От: a7d3  
Дата: 26.07.20 20:54
Оценка:
Здравствуйте, ·, Вы писали:

A>>·>А зачем для DI интерфейсы? Это специфика C# что-ли?

A>>Хотелось бы посмотреть на DI без интерфейсов в случае юнит-тестов на С++.
·>Во-первых, в С++ нет интерфейсов. Так что не очень понятно что конкретно ты имеешь в виду.

В каком плане в С++ нет интерфейсов?
И что именно понимается под интерфейсами, которые якобы не нужны в DI?
Re[6]: Интерфейсы и реализация
От: · Великобритания  
Дата: 26.07.20 21:44
Оценка:
Здравствуйте, a7d3, Вы писали:

A>>>·>А зачем для DI интерфейсы? Это специфика C# что-ли?

A>>>Хотелось бы посмотреть на DI без интерфейсов в случае юнит-тестов на С++.
A>·>Во-первых, в С++ нет интерфейсов. Так что не очень понятно что конкретно ты имеешь в виду.
A>В каком плане в С++ нет интерфейсов?
В языке такого понятия нет.

A>И что именно понимается под интерфейсами, которые якобы не нужны в DI?

Ищем в гугле "di c# example", первая ссылка вот. Там interface ICustomerDataAccess не нужен, и даже немножко вреден.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: Интерфейсы и реализация
От: a7d3  
Дата: 27.07.20 13:38
Оценка:
Здравствуйте, ·, Вы писали:

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


A>>Всегда было и вдруг исчезло? https://en.cppreference.com/w/cpp/language/abstract_class

·>Абстрактный класс это не то же самое, что интерфейс. Врочем, абстрактные классы для DI тоже не нужны.

Спорное утверждение, таковы средства данного языка для реализации такой концепции, как интерфейсы.

A>>>>И что именно понимается под интерфейсами, которые якобы не нужны в DI?

A>>·>Ищем в гугле "di c# example", первая ссылка вот. Там interface ICustomerDataAccess не нужен, и даже немножко вреден.
A>>Чем именно вреден и
·>Насчёт "не нужен" возражений нет?
·>Вреден как минимум тем, что усложняет пример на пустом месте и учит новичков переусложнизму.

В том примере интерфейсы используются для конкретного назначения, если утверждается, что можно проще то, что именно предлагается в качестве альтернативы?
Рассмотрим и тот, и этот варианты, положив на весы в плоскости конкретных, однозначно сформулированных целей и задач.
После чего станет ясно, в каком аспекте, в каком разрезе проигрывает или хорош тот или иной вариант.
Не проблема?
Re[9]: Интерфейсы и реализация
От: Sharov Россия  
Дата: 27.07.20 20:53
Оценка:
Здравствуйте, ·, Вы писали:

·>Насчёт "не нужен" возражений нет?


Для простых приложений и DI не нужен. Так где DI, потребность в интерфейсах в полный рост.

·>Вреден как минимум тем, что усложняет пример на пустом месте и учит новичков переусложнизму.


Новички в DI не полезут.
Кодом людям нужно помогать!
Re[10]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 27.07.20 21:39
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Для простых приложений и DI не нужен. Так где DI, потребность в интерфейсах в полный рост.

Любой объект переданный в конструктор другого объекта (или динамически присвоен, допустим в пропертю) — это и есть та самая Dependency Injection. Как ты понимаешь, данная техника стара, как всё программирование в целом.

S>·>Вреден как минимум тем, что усложняет пример на пустом месте и учит новичков переусложнизму.

S>Новички в DI не полезут.
У новичков нет проблем с DI — они его используют в полный рост, даже не зная как оно называется. Да, куча примеров, предлагают делать интерфейсы, но любой нормальный IoC контейнер может работать с конкретными типами, вдобавок IoC-контейнер — не что иное, как динамически-конфигурируемая фабрика. Несложно понять, что оно нужно далеко не всегда. И даже асп.нет приложении если ты хочешь per-request инстанцирование — интерфейс не нужен.
Re[8]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 27.07.20 21:59
Оценка:
Здравствуйте, a7d3, Вы писали:

A>Всегда было и вдруг исчезло? https://en.cppreference.com/w/cpp/language/abstract_class


Безусловно в этом вопросе ты прав. Более того в C#/Java они играют ту же самую роль, и их использование предпочтительно, если думают не жопой. Отдельно стоящие интерфейсы существуют только для преодоления ограничения в наследовании (только один наследник, но множество интерфейсов). (Само собой интерфейсы накладывают меньше ограничений, что часто играет в плюс.)
Отредактировано 27.07.2020 22:00 Mystic Artifact . Предыдущая версия .
Re[2]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 27.07.20 22:06
Оценка:
Здравствуйте, scf, Вы писали:

scf>- Оформление кода. отдельный интерфейс с джавадоком — это удобная документация для программиста, не загроможденная реализацией.


Получается я не один такой
Автор: Mystic Artifact
Дата: 26.07.20
? Насколько считаешь этот момент важным?

Я довольно часто мечусь по неизвестному коду (даже по своему...), и если это не C++ честно говоря порой офигеваю...
Отредактировано 27.07.2020 22:07 Mystic Artifact . Предыдущая версия . Еще …
Отредактировано 27.07.2020 22:07 Mystic Artifact . Предыдущая версия .
Re[11]: Интерфейсы и реализация
От: Sharov Россия  
Дата: 27.07.20 23:23
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

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


S>>Для простых приложений и DI не нужен. Так где DI, потребность в интерфейсах в полный рост.

MA> Любой объект переданный в конструктор другого объекта (или динамически присвоен, допустим в пропертю) — это и есть та самая Dependency Injection. Как ты понимаешь, данная техника стара, как всё программирование в целом.

Это понятно, речь идет в контексте IoC контейнеров и прочих Composition Root, где граф создается еще на старте. Это нужно далеко не всегда.
Кодом людям нужно помогать!
Re[12]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 27.07.20 23:30
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Это понятно, речь идет в контексте IoC контейнеров и прочих Composition Root, где граф создается еще на старте. Это нужно далеко не всегда.


Ну так там же можно (и нужно) подсунуть не интерфейс, а конкретный класс реализации, если он в принципе в проекте в единственном числе.
Re[12]: Интерфейсы и реализация
От: · Великобритания  
Дата: 28.07.20 00:18
Оценка:
Здравствуйте, a7d3, Вы писали:

A>>> таковы средства данного языка для реализации такой концепции, как интерфейсы.

A>·>Эээ. И что? Средствами даже голого C можно реализовать, например, такую концепцию как рефлексия. И что? Она внезапно там появится?
A>Проводить агитацию за синтаксический сахар того или иного сорта — это оффтоп.
Я не агитировал.

A>Утверждение, что абстрактные классы в С++ не являются интерфейсами — тоже оффтоп.

Ага. Но это я отвечал на твой вопрос про "вдруг исчезло". Онтоп был "абстрактные классы для DI тоже не нужны.". Но это ты просто проигнорировал.

A>·>Я не понял вопрос. Тебе непонятна альтернатива что-ли? Я же уже написал — просто выкинуть interface ICustomerDataAccess и собственно всё. По-моему очевидно что должно получиться. Код что-ли привести?


A>Конкретика должна быть такой:

A>1) для чего именно было решено использовать DI, с какой целью, ради каких задач.
Там по ссылке расписано. Если чего неясно, задавай вопросы.

A>2) какой вариант, в контексте п.1, более вменяем, нежели через интерфейсы.

Третий раз отвечаю — всё то же самое, но без интерфесов. Просто инжектить конкретный класс.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[10]: Интерфейсы и реализация
От: · Великобритания  
Дата: 28.07.20 00:21
Оценка:
Здравствуйте, Sharov, Вы писали:

S>·>Насчёт "не нужен" возражений нет?

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

S>·>Вреден как минимум тем, что усложняет пример на пустом месте и учит новичков переусложнизму.

S>Новички в DI не полезут.
Потому что оно переусложено.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: Интерфейсы и реализация
От: · Великобритания  
Дата: 28.07.20 13:51
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

scf>>- Оформление кода. отдельный интерфейс с джавадоком — это удобная документация для программиста, не загроможденная реализацией.

MA> Получается я не один такой
Автор: Mystic Artifact
Дата: 26.07.20
? Насколько считаешь этот момент важным?

MA> Я довольно часто мечусь по неизвестному коду (даже по своему...), и если это не C++ честно говоря порой офигеваю...
Ну как бы ты прав, момент важный. Но тут ведь как... Такая "удобная документация" — очень легко поддаётся автоматизации. А коли так, то зачем это делать вручную? Как ты упомянул уже есть туча тулзов которые это делают, вот пусть тулзы этим и занимаются, а мне лень.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[13]: Интерфейсы и реализация
От: a7d3  
Дата: 28.07.20 14:02
Оценка:
Здравствуйте, ·, Вы писали:

A>>Конкретика должна быть такой:

A>>1) для чего именно было решено использовать DI, с какой целью, ради каких задач.
·>Там по ссылке расписано. Если чего неясно, задавай вопросы.

Интересует конкретно твой кейс или множество таковых, DI не может быть самоцелью.

A>>2) какой вариант, в контексте п.1, более вменяем, нежели через интерфейсы.

·>Третий раз отвечаю — всё то же самое, но без интерфесов. Просто инжектить конкретный класс.

В четвёртый раз DI не самоцель, конкретные цели с задачами «в студию!» и только после этого обсуждать уже аспекты конкретных подходов.

Например, такой подход, когда в случае C# инжектирую конкретные классы ты будешь использовать System.Object в неявной форме, забыв что там под капотом в плане интерфейсов и занимаясь приведением типов к тому «контракту», который ожидается в жертве инжекта (клиенте).
Re[11]: Интерфейсы и реализация
От: Sharov Россия  
Дата: 29.07.20 17:42
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

S>>·>Вреден как минимум тем, что усложняет пример на пустом месте и учит новичков переусложнизму.

S>>Новички в DI не полезут.
MA> У новичков нет проблем с DI — они его используют в полный рост, даже не зная как оно называется. Да, куча примеров, предлагают делать интерфейсы, но любой нормальный IoC контейнер может работать с конкретными типами, вдобавок IoC-контейнер — не что иное, как динамически-конфигурируемая фабрика. Несложно понять, что оно нужно далеко не всегда. И даже асп.нет приложении если ты хочешь per-request инстанцирование — интерфейс не нужен.

Интерфейс нужен с прицелом на юнит тестирование.
Кодом людям нужно помогать!
Re[15]: Интерфейсы и реализация
От: Sharov Россия  
Дата: 29.07.20 17:54
Оценка:
Здравствуйте, ·, Вы писали:

·>Да причём тут я? Я вообще не рассматривал никакие конкретные кейсы. Я лишь выдвинул тезис, что интерфейсы|абстактные классы к DI никакого отношения не имеют, и чаще всего не нужны.


Подождите, мы DI рассматриваем в контексте IoC контейнеров, где мы в явном виде new никогда не вызываем, а используем,
например, абстрактную фабрику. Ну так вот IoC у меня созвучен с dip, в том плане, что это dip для контроля управления.
А dip говорит, что завязываться на детали (реализации) плохо, лучше завязываться на абстракции. Т.е. мы куда-то встраиваемся
(во фреймворк) через интерфейсы и абстрактные классы.
Кодом людям нужно помогать!
Re[12]: Интерфейсы и реализация
От: Mystic Artifact  
Дата: 29.07.20 21:13
Оценка:
Здравствуйте, Sharov, Вы писали:

S>>>Новички в DI не полезут.

MA>> У новичков нет проблем с DI — они его используют в полный рост, даже не зная как оно называется. Да, куча примеров, предлагают делать интерфейсы, но любой нормальный IoC контейнер может работать с конкретными типами, вдобавок IoC-контейнер — не что иное, как динамически-конфигурируемая фабрика. Несложно понять, что оно нужно далеко не всегда. И даже асп.нет приложении если ты хочешь per-request инстанцирование — интерфейс не нужен.
S>Интерфейс нужен с прицелом на юнит тестирование.

Боже. Я ответил на что-то другое в предыдушем сообщении. Удел новичков — учиться писать хороший / самодостаточный код. Я всегда и часто себя ощущаю новичком, потому что в процессе написания / принятия решения приходится делать микро исследования. Но новичку — топ задача — написать самодостаточный код (работающий, соответствующий требованиям). Рефлексы писать "тестируемый" код — это годы никому не нужных тренировок под один из псевдо-стандартов. Никто и никогда не сможет доказать такими способами тестирования, что код новичка который работает в 100х быстрее, хуже чем тот который как бэ тестируемый, просто ввиду того что нет инжекшн поинтов. На самом деле это возможно доказать или опровергнуть — но потребуется иной подход. Есть вполне конкретные кейсы таких ситуаций: JIT генерирует трудноуловимый неправильный код сопровождающийся рандомными исключениями. Можно просто поискать на гитхабе. Решения, кроме регресс тестов нет, но регрессы появятся только после инциндентов, а
на предложения внедрить "интерфейсы с приметой на тестирование" — будете посланы любым инженером на другой конец света.

Очень грубая аналогия — летать на самолете с заранее раскрывшимся парашютом, с прицелом на то, что он может не раскрыться.
Re: Интерфейсы и реализация
От: rosencrantz США  
Дата: 02.09.20 18:41
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Чем руководствуетесь, принимая решение выделять или не выделять интерфейс из класса, т.е. нужно ли разделить интерфейс и реализацию?


Б>Когда имеется несколько реализация — понятно, интерфейс обычно выделяют.

Б>Но если реализация только одна, для каких классов выделяете интерфейсы, а для каких нет?

Б>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.


Мой ответ будет зависеть от ЯП, потому что в разных ЯП могут быть разные причины хотеть выделить интерфейс.

Отвечу в контексте Java. Выделяю интерфейс когда компонент решает более общую задачу, чем та, которую нужно решить в этой конкретной ситуации. Если во всём коде эта проблема встречается всего 1 раз и именно вот в таком виде, то есть варианты:

1. Заточить решение под эту конкретную ситуацию.
2. Оставить решение общим, и просто, да, использовать его всего 1 раз для этой конкретной ситуации.

Для п.2 интерфейс используется как граница между "вообще решением" и "решением для конкретной ситуации". Например задача — импортировать юзеров из CSV файла — в SQL базу.

interface UserHandler {
  void handleUser(User user);
}

class UserCsvReader {
  void readAllUsers(UserHandler userHandler) {
    ...
  }
}

class BatchInserter<TRow> {
  BatchInserter(String sql, int batchSize) {...}

  void insert(TRow row) {
    if (batch.size < batchSize) {
      batch.add(row);
    } else {
      connection.batchUpdate(sql, batch);
      batch.clear();
    }    
  }
}

class BatchInsertingUserHandler implements UserHandler {
  BatchInsertingUserHandler(BatchInserter<UserRow> batchInserter) {}

  void handleUser(User user) {
    UserRow r = makeRowFromUser(user);
    batchInserter.insert(r);
  }
}

BatchInserter<UserRow> batchInserter = new BatchInserter("insert into Users(...)");
BatchInsertingUserHandler userHandler = new BatchInsertingUserHandler(batchInserter);
UserCsvReader reader = new UsersCsvReader();
reader.readAllUsers(userHandler);


Здесь интерфейс UserHandler используется чтобы отрезать "читание users CSV" от "писания в БД". Интерфейс тут позволяет показать, что вот этот код он конкретно про читание. Он используется при импорте юзеров, но это не сам импорт, а один из инструментов, которые используются импортом.

Если интерфейс выкинуть и оставить только сам BatchInsertingUserHandler, тут возникает много неясностей по поводу как кого переименовать, чтобы не получилось, что холодильник знает о существовании духовки, потому что они оба на кухне.
Re: Интерфейсы и реализация
От: AlexRK  
Дата: 02.09.20 18:59
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Чем руководствуетесь, принимая решение выделять или не выделять интерфейс из класса, т.е. нужно ли разделить интерфейс и реализацию?

Б>Когда имеется несколько реализация — понятно, интерфейс обычно выделяют.
Б>Но если реализация только одна, для каких классов выделяете интерфейсы, а для каких нет?

Не делаю интерфейсов, если это не надо для какой-то конкретной цели (чтобы абстрагироваться без наследования). В 99% случаев мне это не надо. Тестов не пишу.

Б>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.


C#.
Re[3]: Интерфейсы и реализация
От: rosencrantz США  
Дата: 02.09.20 21:20
Оценка:
Здравствуйте, ·, Вы писали:

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


R>>Если интерфейс выкинуть и оставить только сам BatchInsertingUserHandler, тут возникает много неясностей

·>Я не вижу никаких неясностей. Что плохого с именем "UserHandler" для этого класса? Зачем этот префикс BatchInserting?

Там перед кодом дофига написано по поводу мотивации

1. Можно 3 модуля: csv, запись, импорт. Импорт использует первые два.
2. Можно 1 модуль: импорт. Сам умеет csv и запись.
3. Можно эту фичу оформить как часть уже существующего модуля и вообще в один метод всё свернуть.

Я не всегда хочу п.1, но когда я хочу, тогда я делаю "интерфейс с одной реализацией". Мой пример кода — это п.1, ваш — это п.2. О чём мы тут спорим?
Re[4]: Интерфейсы и реализация
От: · Великобритания  
Дата: 02.09.20 22:04
Оценка:
Здравствуйте, rosencrantz, Вы писали:


R>>>Если интерфейс выкинуть и оставить только сам BatchInsertingUserHandler, тут возникает много неясностей

R>·>Я не вижу никаких неясностей. Что плохого с именем "UserHandler" для этого класса? Зачем этот префикс BatchInserting?

R>Там перед кодом дофига написано по поводу мотивации

Я не очень понимаю эту мотивацию, вот и задаю вопросы.

R>1. Можно 3 модуля: csv, запись, импорт. Импорт использует первые два.

R>2. Можно 1 модуль: импорт. Сам умеет csv и запись.
R>3. Можно эту фичу оформить как часть уже существующего модуля и вообще в один метод всё свернуть.

R>Я не всегда хочу п.1, но когда я хочу, тогда я делаю "интерфейс с одной реализацией". Мой пример кода — это п.1, ваш — это п.2. О чём мы тут спорим?

Я пытаюсь выяснить что скрывается за словом "хочу". Ведь должны быть какие то более-менее рациональные критерии выбора. Это и есть вопрос топикстартера.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: Интерфейсы и реализация
От: rosencrantz США  
Дата: 03.09.20 03:39
Оценка:
Здравствуйте, ·, Вы писали:

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


R>>Там перед кодом дофига написано по поводу мотивации

·>Я не очень понимаю эту мотивацию, вот и задаю вопросы.

R>>1. Можно 3 модуля: csv, запись, импорт. Импорт использует первые два.

R>>2. Можно 1 модуль: импорт. Сам умеет csv и запись.
R>>3. Можно эту фичу оформить как часть уже существующего модуля и вообще в один метод всё свернуть.

R>>Я не всегда хочу п.1, но когда я хочу, тогда я делаю "интерфейс с одной реализацией". Мой пример кода — это п.1, ваш — это п.2. О чём мы тут спорим?

·>Я пытаюсь выяснить что скрывается за словом "хочу". Ведь должны быть какие то более-менее рациональные критерии выбора. Это и есть вопрос топикстартера.

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

Хотим мы один модуль или несколько — тут по-моему всё сильно зависит от ментальных моделей и предпочтений. Лезет задачу в голову легко, или со скрипом, или вообще не лезет. Кто-то думает — "мне дают какие-то записи, я группирую их по 100 и делаю batch insert" и ему комфортно. Кто-то думает — "я читаю из csv по 100 строчек и делаю batch insert" и ему комфортно. Кто-то думает — "я получаю какие-то записи и как-то их обрабатываю" (таких обычно бьют, да? ). Модули по-моему откуда-то отсюда вытекают (как и coupling/cohesion, SRP, правило 5+/-2 и прочие всем известные уловки).
Отредактировано 03.09.2020 3:41 rosencrantz . Предыдущая версия . Еще …
Отредактировано 03.09.2020 3:40 rosencrantz . Предыдущая версия .
Re[6]: Интерфейсы и реализация
От: · Великобритания  
Дата: 03.09.20 07:20
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>>>Я не всегда хочу п.1, но когда я хочу, тогда я делаю "интерфейс с одной реализацией". Мой пример кода — это п.1, ваш — это п.2. О чём мы тут спорим?

R>·>Я пытаюсь выяснить что скрывается за словом "хочу". Ведь должны быть какие то более-менее рациональные критерии выбора. Это и есть вопрос топикстартера.

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

Модуль это в смысле jar/dll? Да, там без интерфейса в принцпипе не получится. Я об этом уже писал в самом первом моём сообщении. Но у модуля довольно много накладных расходов и они должны быть меньше, чем выгода от использования модулей.

R>Хотим мы один модуль или несколько — тут по-моему всё сильно зависит от ментальных моделей и предпочтений. Лезет задачу в голову легко, или со скрипом, или вообще не лезет. Кто-то думает — "мне дают какие-то записи, я группирую их по 100 и делаю batch insert" и ему комфортно. Кто-то думает — "я читаю из csv по 100 строчек и делаю batch insert" и ему комфортно. Кто-то думает — "я получаю какие-то записи и как-то их обрабатываю" (таких обычно бьют, да? ). Модули по-моему откуда-то отсюда вытекают (как и coupling/cohesion, SRP, правило 5+/-2 и прочие всем известные уловки).

Тоже опять какие-то субъективные критерии, желание левой пятки...
Модули нужны таки для переиспользования и ограничения зависимостей. Например, чтобы в модуле работы с файловой системой не было доступа к сети. Ещё для организации процесса разработки, например, разработка разными командами или для повышения эффективности процесса билда/тестирования/етс.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[7]: Интерфейсы и реализация
От: rosencrantz США  
Дата: 03.09.20 13:48
Оценка:
Здравствуйте, ·, Вы писали:

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


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

·>Модуль это в смысле jar/dll? Да, там без интерфейса в принцпипе не получится. Я об этом уже писал в самом первом моём сообщении. Но у модуля довольно много накладных расходов и они должны быть меньше, чем выгода от использования модулей.

Не, модуль в смысле — совокупность кода, решающего осмысленную (осмыслимую) задачу (как в "модульное тестирование"). Это может быть 1 класс — какой-нибудь StringBuilder. Может быть несколько — CsvReader и CsvRow.

R>>Хотим мы один модуль или несколько — тут по-моему всё сильно зависит от ментальных моделей и предпочтений. Лезет задачу в голову легко, или со скрипом, или вообще не лезет. Кто-то думает — "мне дают какие-то записи, я группирую их по 100 и делаю batch insert" и ему комфортно. Кто-то думает — "я читаю из csv по 100 строчек и делаю batch insert" и ему комфортно. Кто-то думает — "я получаю какие-то записи и как-то их обрабатываю" (таких обычно бьют, да? ). Модули по-моему откуда-то отсюда вытекают (как и coupling/cohesion, SRP, правило 5+/-2 и прочие всем известные уловки).

·>Тоже опять какие-то субъективные критерии, желание левой пятки...



·>Модули нужны таки для переиспользования и ограничения зависимостей. Например, чтобы в модуле работы с файловой системой не было доступа к сети. Ещё для организации процесса разработки, например, разработка разными командами или для повышения эффективности процесса билда/тестирования/етс.


Терминологическая путаница просто. Я про модули на уровне написания кода, а вы про библиотеки.
Re[7]: Интерфейсы и реализация
От: rosencrantz США  
Дата: 03.09.20 16:03
Оценка:
Здравствуйте, samius, Вы писали:

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


R>>Тс задал вопрос про классы, поэтому я отвечаю про классы. Нужен интерфейс или не нужен (у меня) диктуется тем, хотим мы выделить модуль или нет.


S>


S>Видимо, предлагается проголосовать?


А по делу есть что сказать?
Re[8]: Интерфейсы и реализация
От: · Великобритания  
Дата: 03.09.20 17:43
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>·>Модуль это в смысле jar/dll? Да, там без интерфейса в принцпипе не получится. Я об этом уже писал в самом первом моём сообщении. Но у модуля довольно много накладных расходов и они должны быть меньше, чем выгода от использования модулей.

R>Не, модуль в смысле — совокупность кода, решающего осмысленную (осмыслимую) задачу (как в "модульное тестирование"). Это может быть 1 класс — какой-нибудь StringBuilder. Может быть несколько — CsvReader и CsvRow.
Ок. Вот тогда твой пример с UserHandler непонятен. Я так и не понял накой там interface UserHandler, чем плох просто class UserHandler.
И уж тем более кошмарик BatchInsertingUserHandler, как будто ты имя класса так придумал.
Я для сравнения привёл код без интерфейса — по каким критериям он хуже?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: Интерфейсы и реализация
От: rosencrantz США  
Дата: 03.09.20 19:15
Оценка:
Здравствуйте, ·, Вы писали:

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


R>>·>Модуль это в смысле jar/dll? Да, там без интерфейса в принцпипе не получится. Я об этом уже писал в самом первом моём сообщении. Но у модуля довольно много накладных расходов и они должны быть меньше, чем выгода от использования модулей.

R>>Не, модуль в смысле — совокупность кода, решающего осмысленную (осмыслимую) задачу (как в "модульное тестирование"). Это может быть 1 класс — какой-нибудь StringBuilder. Может быть несколько — CsvReader и CsvRow.
·>Ок. Вот тогда твой пример с UserHandler непонятен. Я так и не понял накой там interface UserHandler, чем плох просто class UserHandler.
·>И уж тем более кошмарик BatchInsertingUserHandler, как будто ты имя класса так придумал.
·>Я для сравнения привёл код без интерфейса — по каким критериям он хуже?

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

Вот сокращённая версия моего кода:

// модуль 1 - "чтение CSV"
interface UserHandler {...}
class UserCsvReader {...}
// конец модуля 1

// модуль 2 - "запись в базу батчами"
class BatchInserter<TRow> {...}
// конец модуля 2

// модуль 3 - "импорт" (использует модуль "чтение CSV" и модуль "запись в базу батчами")
class BatchInsertingUserHandler implements UserHandler {
  BatchInsertingUserHandler(BatchInserter<UserRow> batchInserter) {...}
}

BatchInserter<UserRow> batchInserter = new BatchInserter("insert into Users(...)");
BatchInsertingUserHandler userHandler = new BatchInsertingUserHandler(batchInserter);
UserCsvReader reader = new UsersCsvReader();
reader.readAllUsers(userHandler);
// конец модуля 3


Интерфейс UserHandler здесь нужен для того, чтобы показать границу между модулем 1 (как мы читаем CSV) и модулем 3 (когда мы читаем CSV, что мы с этим делаем). Модуль 1 в этом случае именно вполне самостоятельный модуль. Его можно без изменений переиспользовать для например написания конвертора из CSV — в XML. Но это не очень важно. Важно что самодостаточная концепция изолирована от остального мира То же самое, что ваша иллюстрация про "файловая система не должна знать про сеть" — у меня "читалка csv не должна знать про писалку в бд", просто масштаб другой.

В вашей версии если UserHandler превращается в BatchInsertingUserHandler, модуль 1 исчезает и его содержимое становится частью модуля 3. Конвертор CSV->XML вы уже не напишете без правок кода, но такой цели и не было. Но вообще чтение CSV у вас теперь гвоздями прибито к вставке в базу.
Re[10]: Интерфейсы и реализация
От: · Великобритания  
Дата: 04.09.20 09:49
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>·>Ок. Вот тогда твой пример с UserHandler непонятен. Я так и не понял накой там interface UserHandler, чем плох просто class UserHandler.

R>·>И уж тем более кошмарик BatchInsertingUserHandler, как будто ты имя класса так придумал.
R>·>Я для сравнения привёл код без интерфейса — по каким критериям он хуже?

R>Он не обязательно хуже, он просто не обладает теми свойствами, которыми обладает мой вариант. Плохо это или хорошо — я ж как раз пытаюсь донести — зависит от восприятия автора/читателя.

Ок. Каким образом в коде выражается это разбиение на модули? Ну т.е. представь себе ты написал код, закоммитил, я открываю твой код и я там увижу эти модули? Как читатель узнает твоё авторское восприятие этого кода?

R>Вот сокращённая версия моего кода:

// модуль 3 - "импорт" (использует модуль "чтение CSV" и модуль "запись в базу батчами")
class BatchInsertingUserHandler implements UserHandler {
  BatchInsertingUserHandler(BatchInserter<UserRow> batchInserter) {...}
}

 --- вот здесь явно видна ещё граница между модулями. "модуль импорт" и "модуль сборки конечного приложения"
 --- т.к. это явно просто wiring code, composition root.

BatchInserter<UserRow> batchInserter = new BatchInserter("insert into Users(...)");
BatchInsertingUserHandler userHandler = new BatchInsertingUserHandler(batchInserter);
UserCsvReader reader = new UsersCsvReader();
reader.readAllUsers(userHandler);
// конец модуля 3


R>Интерфейс UserHandler здесь нужен для того, чтобы показать границу между модулем 1 (как мы читаем CSV) и модулем 3 (когда мы читаем CSV, что мы с этим делаем). Модуль 1 в этом случае именно вполне самостоятельный модуль. Его можно без изменений переиспользовать для например написания конвертора из CSV — в XML. Но это не очень важно. Важно что самодостаточная концепция изолирована от остального мира То же самое, что ваша иллюстрация про "файловая система не должна знать про сеть" — у меня "читалка csv не должна знать про писалку в бд", просто масштаб другой.

Тут я топлю за принцип YAGNI. Вот когда нам не станет хватать, что наш class UserHandler пишет батчами в бд, вот тогда мы отрефакторим код и у нас получится interface UserHandler и реализующие его class DbUserWriter, class XmlUserWriter — с не вымученными enterprisified именами, а явно продиктованными новыми появившимися требованиями.

R>В вашей версии если UserHandler превращается в BatchInsertingUserHandler, модуль 1 исчезает и его содержимое становится частью модуля 3. Конвертор CSV->XML вы уже не напишете без правок кода, но такой цели и не было. Но вообще чтение CSV у вас теперь гвоздями прибито к вставке в базу.

Модуль 1 не исчезнет, а станет состоять из только лишь класса UserCsvReader.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[11]: Интерфейсы и реализация
От: rosencrantz США  
Дата: 04.09.20 13:49
Оценка:
Здравствуйте, ·, Вы писали:

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


R>>·>Ок. Вот тогда твой пример с UserHandler непонятен. Я так и не понял накой там interface UserHandler, чем плох просто class UserHandler.

R>>·>И уж тем более кошмарик BatchInsertingUserHandler, как будто ты имя класса так придумал.
R>>·>Я для сравнения привёл код без интерфейса — по каким критериям он хуже?

R>>Он не обязательно хуже, он просто не обладает теми свойствами, которыми обладает мой вариант. Плохо это или хорошо — я ж как раз пытаюсь донести — зависит от восприятия автора/читателя.

·>Ок. Каким образом в коде выражается это разбиение на модули? Ну т.е. представь себе ты написал код, закоммитил, я открываю твой код и я там увижу эти модули? Как читатель узнает твоё авторское восприятие этого кода?

Ну серьёзно? "Ну т.е. представь себе ты написал код, закоммитил, <скип> Как читатель узнает твоё авторское восприятие этого кода?" — про это пишутся сотни книжек и ведутся регулярные срачи на рсдн. Какой ответ вы ожидаете услышать?

R>>Интерфейс UserHandler здесь нужен для того, чтобы показать границу между модулем 1 (как мы читаем CSV) и модулем 3 (когда мы читаем CSV, что мы с этим делаем). Модуль 1 в этом случае именно вполне самостоятельный модуль. Его можно без изменений переиспользовать для например написания конвертора из CSV — в XML. Но это не очень важно. Важно что самодостаточная концепция изолирована от остального мира То же самое, что ваша иллюстрация про "файловая система не должна знать про сеть" — у меня "читалка csv не должна знать про писалку в бд", просто масштаб другой.

·>Тут я топлю за принцип YAGNI.

А не вкусовщина ли это?

·>Вот когда нам не станет хватать, что наш class UserHandler пишет батчами в бд, вот тогда мы отрефакторим код и у нас получится interface UserHandler и реализующие его class DbUserWriter, class XmlUserWriter — с не вымученными enterprisified именами, а явно продиктованными новыми появившимися требованиями.


Я совершенно за YAGNI, но только там где противопоставляются более накрученное решение и более простое. Добавление 1 интерфейса не делает решение более сложным — ни в восприятии, ни в реализации, ни в сопровождении. Для тех, кто не сечёт фишку, решение по сложности — такое же, как и без интерфейса, для тех, кто сечёт — это "граница интерфейса, точка расширения"

Можно провести аналогию с поясняющей переменной:

if (person.age >= 21) {
  ...
}


Всё тут в порядке? YAGNI не чешется?

bool isAllowedToDrinkBear = person.age >= 21;
if (isAllowedToDrinkBear) {
  ...
}


Нужна поясняющая переменная? Или YAGNI и потом отрефакторим? Вносит она проблемы? Увеличивает сложность экспоненциально? Что насчёт поясняющей функции?

if (isAllowedToDrinkBear(person)) {
  ...
}


Тут как? YAGNI? Что насчёт выноса этой функции в отдельный класс?

if (bearDrinkingDecisionMaker.isAllowedToDrinkBear(person)) {
  ...
}


Во всех случаях (кроме первого) появление имени "isAllowedToDrinkBear" — это тот самый ввод интерфейса.

R>>В вашей версии если UserHandler превращается в BatchInsertingUserHandler, модуль 1 исчезает и его содержимое становится частью модуля 3. Конвертор CSV->XML вы уже не напишете без правок кода, но такой цели и не было. Но вообще чтение CSV у вас теперь гвоздями прибито к вставке в базу.

·>Модуль 1 не исчезнет, а станет состоять из только лишь класса UserCsvReader.

Это не модуль. Модуль — это когда можно провести границу. В вашем случае границу провести не получится
Re[12]: Интерфейсы и реализация
От: · Великобритания  
Дата: 04.09.20 17:20
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>>>Он не обязательно хуже, он просто не обладает теми свойствами, которыми обладает мой вариант. Плохо это или хорошо — я ж как раз пытаюсь донести — зависит от восприятия автора/читателя.

R>·>Ок. Каким образом в коде выражается это разбиение на модули? Ну т.е. представь себе ты написал код, закоммитил, я открываю твой код и я там увижу эти модули? Как читатель узнает твоё авторское восприятие этого кода?
R>Ну серьёзно? "Ну т.е. представь себе ты написал код, закоммитил, <скип> Как читатель узнает твоё авторское восприятие этого кода?" — про это пишутся сотни книжек и ведутся регулярные срачи на рсдн. Какой ответ вы ожидаете услышать?
Серьёзно. Так я и пытаюсь обратить внимание важность именно явных моментов. Скажем, твой изначальный код я понял без пояснений что там есть. А вот твои "модули" я там не увидел, а после того как ты их указал — я мысленно увидел и другие варианты разбиения. Значит это только лишь твоя субъекивная картина. Т.е. ты ради своей личной субъективной картины усложняешь код, внося туда лишние конструкции языка и запутывая читателя, т.к. нет ничего явного, что бы позволило передать авторское восприятие.

R>·>Тут я топлю за принцип YAGNI.

R>А не вкусовщина ли это?
Писать простой код — вкусовщина?

R>·>Вот когда нам не станет хватать, что наш class UserHandler пишет батчами в бд, вот тогда мы отрефакторим код и у нас получится interface UserHandler и реализующие его class DbUserWriter, class XmlUserWriter — с не вымученными enterprisified именами, а явно продиктованными новыми появившимися требованиями.

R>Я совершенно за YAGNI, но только там где противопоставляются более накрученное решение и более простое. Добавление 1 интерфейса не делает решение более сложным — ни в восприятии, ни в реализации, ни в сопровождении. Для тех, кто не сечёт фишку, решение по сложности — такое же, как и без интерфейса, для тех, кто сечёт — это "граница интерфейса, точка расширения"
В реализации сложность — как минимум у тебя появляется необходимость придумывать два имени для сущностей. BatchInsertingUserHandler — выглядит жутко.
В сопровождении — выяснить что делает handleUser — надо прыгать между местом использования между интерфейсом и классом.

R>Можно провести аналогию с поясняющей переменной:


R>
R>if (person.age >= 21) {
R>  ...
R>}
R>

R>Всё тут в порядке? YAGNI не чешется?
Тут magic number чешется как минимум. Понятно, что другие принципы тоже важны. Вот какому принципу ты следуешь, когда хочешь сделать тот интерфейс — я и хочу понять.

R>
R>bool isAllowedToDrinkBear = person.age >= 21;
R>if (isAllowedToDrinkBear) {
R>  ...
R>}
R>

R>Нужна поясняющая переменная? Или YAGNI и потом отрефакторим? Вносит она проблемы? Увеличивает сложность экспоненциально? Что насчёт поясняющей функции?
Либо так, либо "const DRINK_BEER_LEGAL_AGE = 21". Т.к. из кода совершенно непонятно какой семантикой обладает "age >= 21". То ли пиво, то ли ношение оружия, толи порно. В твоём примере с интерфейсом я неоднозначностей не заметил, так что не очень хорошая аналогия.
Кстати, медведей лучше не пить, даже в 21.

R>
R>if (isAllowedToDrinkBear(person)) {
R>  ...
R>}
R>

R>Тут как? YAGNI? Что насчёт выноса этой функции в отдельный класс?
Если код используется из разных частей кода, то имеет смысл вынести в функцию, да. А иначе, ясен пень, надо остановиться на предыдущем шаге.

R>
R>if (bearDrinkingDecisionMaker.isAllowedToDrinkBear(person)) {
R>  ...
R>}
R>

R>Во всех случаях (кроме первого) появление имени "isAllowedToDrinkBear" — это тот самый ввод интерфейса.
То же самое, для переиспользования или если isAllowedToDrinkBear имеет смысл покрыть отдельно тестами. Правда конкретно для тривиального выражения вроде "age >= 21" я бы точно не стал тестами отдельно покрывать.

R>>>В вашей версии если UserHandler превращается в BatchInsertingUserHandler, модуль 1 исчезает и его содержимое становится частью модуля 3. Конвертор CSV->XML вы уже не напишете без правок кода, но такой цели и не было. Но вообще чтение CSV у вас теперь гвоздями прибито к вставке в базу.

R>·>Модуль 1 не исчезнет, а станет состоять из только лишь класса UserCsvReader.
R>Это не модуль. Модуль — это когда можно провести границу. В вашем случае границу провести не получится
Я значит не понял принцип по которому ты проводишь границы. По-моему всё нормально проводится.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[13]: Интерфейсы и реализация
От: rosencrantz США  
Дата: 04.09.20 21:26
Оценка:
Здравствуйте, ·, Вы писали:

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


R>>>>Он не обязательно хуже, он просто не обладает теми свойствами, которыми обладает мой вариант. Плохо это или хорошо — я ж как раз пытаюсь донести — зависит от восприятия автора/читателя.

R>>·>Ок. Каким образом в коде выражается это разбиение на модули? Ну т.е. представь себе ты написал код, закоммитил, я открываю твой код и я там увижу эти модули? Как читатель узнает твоё авторское восприятие этого кода?
R>>Ну серьёзно? "Ну т.е. представь себе ты написал код, закоммитил, <скип> Как читатель узнает твоё авторское восприятие этого кода?" — про это пишутся сотни книжек и ведутся регулярные срачи на рсдн. Какой ответ вы ожидаете услышать?
·>Серьёзно. Так я и пытаюсь обратить внимание важность именно явных моментов. Скажем, твой изначальный код я понял без пояснений что там есть. А вот твои "модули" я там не увидел, а после того как ты их указал — я мысленно увидел и другие варианты разбиения. Значит это только лишь твоя субъекивная картина. Т.е. ты ради своей личной субъективной картины усложняешь код, внося туда лишние конструкции языка и запутывая читателя, т.к. нет ничего явного, что бы позволило передать авторское восприятие.

Именно субъективная ментальная модель автора и определяет на какие части будет порезан код, и как эти части будут взаимодействовать. Олимпиадник будет строить ментальную модель на основе своего видения важного: корректность, скорость, память, но не сопровождаемость или тестируемость. Не-олимпиадники вполне будут руководствоваться сопровождаемостью и тестируемостью, но всё равно остаётся много свободы. Если в ментальной модели автора есть самостоятельный компонент X, отрезанный от всего остального мира, автор может попытаться эту самостоятельность и отрезанность как-то показать.

Один из вариантов представления этого в коде — использование компонентом X интерфейса к внешнему миру вместо самого внешнего мира. Это частный случай https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8/%D0%B7%D0%B0%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8 — для переиспользования X в нескольких разных "мирах" не нужно делать никаких правок в самом X, нужно просто добавлять новые реализации интерфейса и подсовывать их в X. Мне не надо ждать пока меня петух в задницу клюнет, чтобы увидеть разницу между связью "чтение CSV" — "запись в БД" (её нет) и, "чтение CSV" — "запись CSV" (механизмы для (де)экранирования строк наверняка будут одни и те же — и там, и там) Вы разницу видите?

R>>·>Тут я топлю за принцип YAGNI.

R>>А не вкусовщина ли это?
·>Писать простой код — вкусовщина?

Да вы так с этим YAGNI носитесь, как будто это правда что-то. Ну кто-то предложил принцип, и дальше что? За ним нет исследований с цифрами, экспериментальных подтверждений, это просто "экспертное мнение". С этим мнением можно согласиться, можно не согласиться, можно иметь в виду, можно поспорить о границах применимости. OCP вон тоже принцип — "пишите код так, чтобы только дописывать и никогда не трогать уже написанное" — я не знаю если вы вообще его принимаете, но очевидно он у вас глубоко не на первом месте.

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

R>>·>Вот когда нам не станет хватать, что наш class UserHandler пишет батчами в бд, вот тогда мы отрефакторим код и у нас получится interface UserHandler и реализующие его class DbUserWriter, class XmlUserWriter — с не вымученными enterprisified именами, а явно продиктованными новыми появившимися требованиями.

R>>Я совершенно за YAGNI, но только там где противопоставляются более накрученное решение и более простое. Добавление 1 интерфейса не делает решение более сложным — ни в восприятии, ни в реализации, ни в сопровождении. Для тех, кто не сечёт фишку, решение по сложности — такое же, как и без интерфейса, для тех, кто сечёт — это "граница интерфейса, точка расширения"
·>В реализации сложность — как минимум у тебя появляется необходимость придумывать два имени для сущностей. BatchInsertingUserHandler — выглядит жутко.
·>В сопровождении — выяснить что делает handleUser — надо прыгать между местом использования между интерфейсом и классом.

Странно, у меня такая же BatchInsertingUserHandler — и не болит Если видел одним глазом в коде интерфейс UserHandler, все классы с таким суффиксом автоматически воспринимаются как реализации этого интерфейса. "BatchInserting" описывает чем примечательна эта конкретная реализация.

R>>>>В вашей версии если UserHandler превращается в BatchInsertingUserHandler, модуль 1 исчезает и его содержимое становится частью модуля 3. Конвертор CSV->XML вы уже не напишете без правок кода, но такой цели и не было. Но вообще чтение CSV у вас теперь гвоздями прибито к вставке в базу.

R>>·>Модуль 1 не исчезнет, а станет состоять из только лишь класса UserCsvReader.
R>>Это не модуль. Модуль — это когда можно провести границу. В вашем случае границу провести не получится
·>Я значит не понял принцип по которому ты проводишь границы. По-моему всё нормально проводится.

Ну давайте совсем в лоб. Хорошая граница — это когда код читалки CSV идёт в отдельную библиотеку usercsvreader.dll и, блин, не тянет за собой драйвер Mysql В моём понимании адекватный код пишется именно с этим ограничением в голове. В версии с интерфейсом оно будет именно так — взял и перетащил, никаких правок в коде.
Re[14]: Интерфейсы и реализация
От: · Великобритания  
Дата: 04.09.20 22:25
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>·>Серьёзно. Так я и пытаюсь обратить внимание важность именно явных моментов. Скажем, твой изначальный код я понял без пояснений что там есть. А вот твои "модули" я там не увидел, а после того как ты их указал — я мысленно увидел и другие варианты разбиения. Значит это только лишь твоя субъекивная картина. Т.е. ты ради своей личной субъективной картины усложняешь код, внося туда лишние конструкции языка и запутывая читателя, т.к. нет ничего явного, что бы позволило передать авторское восприятие.

R>Именно субъективная ментальная модель автора и определяет на какие части будет порезан код, и как эти части будут взаимодействовать. Олимпиадник будет строить ментальную модель на основе своего видения важного: корректность, скорость, память, но не сопровождаемость или тестируемость. Не-олимпиадники вполне будут руководствоваться сопровождаемостью и тестируемостью, но всё равно остаётся много свободы. Если в ментальной модели автора есть самостоятельный компонент X, отрезанный от всего остального мира, автор может попытаться эту самостоятельность и отрезанность как-то показать.
Ну олимпиадники это не в тему, по-моему было очевидно, что я говорю в контексте промышленного программирования, когда цель — профессионально написанный код, а не художественное произведение, раскрывающее внутренний мир автора. В этом случае идеал когда модель диктуется бизнес-требованиями, а не свободой полёта мысли.

R>Один из вариантов представления этого в коде — использование компонентом X интерфейса к внешнему миру вместо самого внешнего мира. Это частный случай https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8/%D0%B7%D0%B0%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8 — для переиспользования X в нескольких разных "мирах" не нужно делать никаких правок в самом X, нужно просто добавлять новые реализации интерфейса и подсовывать их в X. Мне не надо ждать пока меня петух в задницу клюнет, чтобы увидеть разницу между связью "чтение CSV" — "запись в БД" (её нет) и, "чтение CSV" — "запись CSV" (механизмы для (де)экранирования строк наверняка будут одни и те же — и там, и там) Вы разницу видите?

Нет. У тебя был код чтения csv который дёргал handleUser — и ему было по барабану что там пишется в БД. И вообще разницы не было это метод интерфейса или класса. В реализации handleUser писалась БД и было по барабану кто дёргал этот метод. Добавление интерфейса в случае ровно одной реалицаии ничего не меняет.

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

R>·>Писать простой код — вкусовщина?

R>Да вы так с этим YAGNI носитесь, как будто это правда что-то. Ну кто-то предложил принцип, и дальше что? За ним нет исследований с цифрами, экспериментальных подтверждений, это просто "экспертное мнение". С этим мнением можно согласиться, можно не согласиться, можно иметь в виду, можно поспорить о границах применимости. OCP вон тоже принцип — "пишите код так, чтобы только дописывать и никогда не трогать уже написанное" — я не знаю если вы вообще его принимаете, но очевидно он у вас глубоко не на первом месте.
Рефакторинг "class UserHandler" -> "interface UserHandler" + "class DbUserHandler" не трогает уже написанное и с OCP не конфликтует.

R>Ну и прежде чем называть код простым, поделитесь вашими критериями простоты.

При прочих равных — чем короче, тем проще. Твой код длиннее, а в остальном я разницы не вижу.

R>·>В реализации сложность — как минимум у тебя появляется необходимость придумывать два имени для сущностей. BatchInsertingUserHandler — выглядит жутко.

R>·>В сопровождении — выяснить что делает handleUser — надо прыгать между местом использования между интерфейсом и классом.
R>Странно, у меня такая же BatchInsertingUserHandler — и не болит Если видел одним глазом в коде интерфейс UserHandler, все классы с таким суффиксом автоматически воспринимаются как реализации этого интерфейса. "BatchInserting" описывает чем примечательна эта конкретная реализация.
Ок, отдельный флейм не буду начинать... именование — может быть дело вкуса, могу согласится.

R>>>·>Модуль 1 не исчезнет, а станет состоять из только лишь класса UserCsvReader.

R>>>Это не модуль. Модуль — это когда можно провести границу. В вашем случае границу провести не получится
R>·>Я значит не понял принцип по которому ты проводишь границы. По-моему всё нормально проводится.

R>Ну давайте совсем в лоб. Хорошая граница — это когда код читалки CSV идёт в отдельную библиотеку usercsvreader.dll и, блин, не тянет за собой драйвер Mysql В моём понимании адекватный код пишется именно с этим ограничением в голове.

А почему ты именно тут границу ставишь? Может лучше граница это когда код идёт в отдельный асинхронный скалируемый микросервис? Ну json там, калбеки, ретраи, таймауты, рекавери, балансинг... Библиотеки нынче уже не модно.
Или всё-таки лучше придерживаться необходимого минимума? "Всё должно быть изложено так просто, как только возможно, но не проще".

R> В версии с интерфейсом оно будет именно так — взял и перетащил, никаких правок в коде.

А ты сделай diff между твоим
Автор: rosencrantz
Дата: 02.09.20
кодом и моим
Автор: ·
Дата: 02.09.20
. И заметь — реальных правок нет никаких, просто удалены лишние символы.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[15]: Интерфейсы и реализация
От: rosencrantz США  
Дата: 04.09.20 23:43
Оценка:
Здравствуйте, ·, Вы писали:

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



R>>Именно субъективная ментальная модель автора и определяет на какие части будет порезан код, и как эти части будут взаимодействовать. Олимпиадник будет строить ментальную модель на основе своего видения важного: корректность, скорость, память, но не сопровождаемость или тестируемость. Не-олимпиадники вполне будут руководствоваться сопровождаемостью и тестируемостью, но всё равно остаётся много свободы. Если в ментальной модели автора есть самостоятельный компонент X, отрезанный от всего остального мира, автор может попытаться эту самостоятельность и отрезанность как-то показать.

·>Ну олимпиадники это не в тему, по-моему было очевидно, что я говорю в контексте промышленного программирования, когда цель — профессионально написанный код,

Это у вас такая цель? Что это вообще такое — "профессионально написанный код" расскажете? У меня такой цели нет. В моей реальности мы все принимаем технические решения и получаем за это зарплату. Что там профессиональный код, что непрофессиональный — это каждый сам себе перед зеркалом. Можно прикрываться всякими там принципами, но на каждый принцип есть такой же замечательный противоположный принцип.

·> а не художественное произведение, раскрывающее внутренний мир автора.


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

·> В этом случае идеал когда модель диктуется бизнес-требованиями, а не свободой полёта мысли.


Ох нелегко вам приходится — сидите там в цепях и гребёте. Серьёзно — вам бизнес даёт требования по сопровождаемости, тестируемости и всей прочей ерунде? Мне интересно. Вот прямо приходит бизнес и говорит: "если ты добавишь интерфейс, мы потратим лишние $100 долларов на оплату твоей работы, но скорее всего они не вернутся, поэтому не добавляй интерфейса" — это вот это называется "диктуется бизнес-требованиями"? Если скажут тесты не писать, даже глазом не моргнёте, лишь бы бизнес счастлив был?

R>>Один из вариантов представления этого в коде — использование компонентом X интерфейса к внешнему миру вместо самого внешнего мира. Это частный случай https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8/%D0%B7%D0%B0%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8 — для переиспользования X в нескольких разных "мирах" не нужно делать никаких правок в самом X, нужно просто добавлять новые реализации интерфейса и подсовывать их в X. Мне не надо ждать пока меня петух в задницу клюнет, чтобы увидеть разницу между связью "чтение CSV" — "запись в БД" (её нет) и, "чтение CSV" — "запись CSV" (механизмы для (де)экранирования строк наверняка будут одни и те же — и там, и там) Вы разницу видите?

·>Нет. У тебя был код чтения csv который дёргал handleUser — и ему было по барабану что там пишется в БД.

Да, у меня именно так и было.

·>И вообще разницы не было это метод интерфейса или класса. В реализации handleUser писалась БД и было по барабану кто дёргал этот метод. Добавление интерфейса в случае ровно одной реалицаии ничего не меняет.


не распарсил.

·>Не путай, "интерфейс" как абстрактное понятие модели кода и "интерфейс" как конструкция яп — разные понятия. Набор публичных методов класса это уже интерфейс.


Ды вроде не путаю.

R>>·>Писать простой код — вкусовщина?

R>>Да вы так с этим YAGNI носитесь, как будто это правда что-то. Ну кто-то предложил принцип, и дальше что? За ним нет исследований с цифрами, экспериментальных подтверждений, это просто "экспертное мнение". С этим мнением можно согласиться, можно не согласиться, можно иметь в виду, можно поспорить о границах применимости. OCP вон тоже принцип — "пишите код так, чтобы только дописывать и никогда не трогать уже написанное" — я не знаю если вы вообще его принимаете, но очевидно он у вас глубоко не на первом месте.
·>Рефакторинг "class UserHandler" -> "interface UserHandler" + "class DbUserHandler" не трогает уже написанное и с OCP не конфликтует.

Правки кода не трогают уже написанное. Окей

R>>Ну и прежде чем называть код простым, поделитесь вашими критериями простоты.

·>При прочих равных — чем короче, тем проще. Твой код длиннее, а в остальном я разницы не вижу.

"Я разницы не вижу" — я не готов искать истину в таком базисе

R>>Ну давайте совсем в лоб. Хорошая граница — это когда код читалки CSV идёт в отдельную библиотеку usercsvreader.dll и, блин, не тянет за собой драйвер Mysql В моём понимании адекватный код пишется именно с этим ограничением в голове.

·>А почему ты именно тут границу ставишь? Может лучше граница это когда код идёт в отдельный асинхронный скалируемый микросервис? Ну json там, калбеки, ретраи, таймауты, рекавери, балансинг... Библиотеки нынче уже не модно.

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

·>Или всё-таки лучше придерживаться необходимого минимума? "Всё должно быть изложено так просто, как только возможно, но не проще".


R>> В версии с интерфейсом оно будет именно так — взял и перетащил, никаких правок в коде.

·>А ты сделай diff между твоим
Автор: rosencrantz
Дата: 02.09.20
кодом и моим
Автор: ·
Дата: 02.09.20
. И заметь — реальных правок нет никаких, просто удалены лишние символы.


Какое интересное наблюдение
Re[16]: Интерфейсы и реализация
От: · Великобритания  
Дата: 05.09.20 12:52
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>·>Ну олимпиадники это не в тему, по-моему было очевидно, что я говорю в контексте промышленного программирования, когда цель — профессионально написанный код,

R>Это у вас такая цель? Что это вообще такое — "профессионально написанный код" расскажете? У меня такой цели нет. В моей реальности мы все принимаем технические решения и получаем за это зарплату. Что там профессиональный код, что непрофессиональный — это каждый сам себе перед зеркалом. Можно прикрываться всякими там принципами, но на каждый принцип есть такой же замечательный противоположный принцип.
Это понятно, я просто пытаюсь вернуть тебя в русло обсуждения. Твои возражения основанные об особенностях олимпиадного программирования — вообще не в тему. В олимпиадном программировании сабж вообще по барабану, там вряд ли вообще кто-либо даже классы использует, не говоря об интерфейсах.

R>·> а не художественное произведение, раскрывающее внутренний мир автора.

R>Ах, как завернул. Давай, плюй мне в душу. Мой внутренний мир он не хочет, видите ли Это называется навыки декомпозиции, а не внутренний мир. Есть один фундаментальный принцип — сложность надо резать на кусочки. Как именно резать — на это есть сотня противоречивых принципов сомнительного происхождения с неочевидной областью применимости, есть личный опыт, есть тараканы. Правильного ответа нет никогда. Есть только большое множество совсем неправильных, и меньшее множество удовлетворительных с плюсами и минусами.
С этим я не спорю. Резать надо, чтобы упростить поддержку кода, а значит удешевить разработку, что и есть одна из целей промышленного программирования. Суть моих возражений, что в твоём примере введения интерфейса разрез-то был, а вот сложности не было. В итоге сложность только увеличилась, а значит цель не достигнута.

R>·> В этом случае идеал когда модель диктуется бизнес-требованиями, а не свободой полёта мысли.

R>Ох нелегко вам приходится — сидите там в цепях и гребёте. Серьёзно — вам бизнес даёт требования по сопровождаемости, тестируемости и всей прочей ерунде? Мне интересно. Вот прямо приходит бизнес и говорит: "если ты добавишь интерфейс, мы потратим лишние $100 долларов на оплату твоей работы, но скорее всего они не вернутся, поэтому не добавляй интерфейса" — это вот это называется "диктуется бизнес-требованиями"? Если скажут тесты не писать, даже глазом не моргнёте, лишь бы бизнес счастлив был?
В каком-то смысле да, ибо без бизнеса программисты не нужны, бизнес нас кормит. Конечно, бизнес так не говорит, он словьёв то таких не знает. Он измеряет деньгами. Если кто-то наворотил кучу лишнего кода с плохой сопровождаемостью и тестируемостью, то это выльется определённое количество денег на поддержку и внесение изменений. Вот и когда бизнес будет выбирать с кем работать — он выберет того, кто на языке бизнеса тратит меньше денег, т.е. в переводе на программистский язык — пишет простой и тестируемый код.

R>>>Один из вариантов представления этого в коде — использование компонентом X интерфейса к внешнему миру вместо самого внешнего мира. Это частный случай https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8/%D0%B7%D0%B0%D0%BA%D1%80%D1%8B%D1%82%D0%BE%D1%81%D1%82%D0%B8 — для переиспользования X в нескольких разных "мирах" не нужно делать никаких правок в самом X, нужно просто добавлять новые реализации интерфейса и подсовывать их в X. Мне не надо ждать пока меня петух в задницу клюнет, чтобы увидеть разницу между связью "чтение CSV" — "запись в БД" (её нет) и, "чтение CSV" — "запись CSV" (механизмы для (де)экранирования строк наверняка будут одни и те же — и там, и там) Вы разницу видите?

R>·>Нет. У тебя был код чтения csv который дёргал handleUser — и ему было по барабану что там пишется в БД.
R>Да, у меня именно так и было.
Верно. И пофиг, что UserHandle будет class или interface.

R>·>И вообще разницы не было это метод интерфейса или класса. В реализации handleUser писалась БД и было по барабану кто дёргал этот метод. Добавление интерфейса в случае ровно одной реалицаии ничего не меняет.

R> не распарсил.
Тело метода handleUser пишет в бд. И кто вызывает метод — тоже пофиг. Т.е. твои "модули" можно выделить точно так же даже если не будет interface.

R>·>Не путай, "интерфейс" как абстрактное понятие модели кода и "интерфейс" как конструкция яп — разные понятия. Набор публичных методов класса это уже интерфейс.

R>Ды вроде не путаю.
csv и бд в том примере взаимодействуют через интерфейс, вне зависимости от того, есть ли "interface" как в твоём коде или напрямую используется "class" как в моём коде. Следовательно из двух решений надо выбрать то, которое короче.

R>>>Да вы так с этим YAGNI носитесь, как будто это правда что-то. Ну кто-то предложил принцип, и дальше что? За ним нет исследований с цифрами, экспериментальных подтверждений, это просто "экспертное мнение". С этим мнением можно согласиться, можно не согласиться, можно иметь в виду, можно поспорить о границах применимости. OCP вон тоже принцип — "пишите код так, чтобы только дописывать и никогда не трогать уже написанное" — я не знаю если вы вообще его принимаете, но очевидно он у вас глубоко не на первом месте.

R>·>Рефакторинг "class UserHandler" -> "interface UserHandler" + "class DbUserHandler" не трогает уже написанное и с OCP не конфликтует.
R>Правки кода не трогают уже написанное. Окей
OCP вообще-то об изменениях, меняющих поведение. Рефакторинг по определению — изменение не меняющее поведение.

R>>>Ну и прежде чем называть код простым, поделитесь вашими критериями простоты.

R>·>При прочих равных — чем короче, тем проще. Твой код длиннее, а в остальном я разницы не вижу.
R>"Я разницы не вижу" — я не готов искать истину в таком базисе
В смысле? Тебе лишь стоит показать, что разница есть.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: Интерфейсы и реализация
От: Barbar1an Украина  
Дата: 05.09.20 15:49
Оценка:
Здравствуйте, Sharov, Вы писали:

S>.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.


причем максимум у них 2 реализации а если не заморчиваться с тестами то ваще 1, сам пишу, но это хрень и дичь
Я изъездил эту страну вдоль и поперек, общался с умнейшими людьми и я могу вам ручаться в том, что обработка данных является лишь причудой, мода на которую продержится не более года. (с) Эксперт, авторитет и профессионал из 1957 г.
Отредактировано 05.09.2020 15:50 Barbar1an . Предыдущая версия .
Re: Интерфейсы и реализация
От: Shtole  
Дата: 08.09.20 17:00
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Чем руководствуетесь, принимая решение выделять или не выделять интерфейс из класса, т.е. нужно ли разделить интерфейс и реализацию?


Б>Когда имеется несколько реализация — понятно, интерфейс обычно выделяют.

Б>Но если реализация только одна, для каких классов выделяете интерфейсы, а для каких нет?

В тех случаях, когда может/должна появиться независимая реализация. Наличие интерфейса при одной-единственной реализации это, собственно, способ сказать читателю кода: «Появится другая, возможно, я знаю, какая, а возможно — имею об этом лишь смутное представление».

Важными считаю 2 момента:

1. Иногда реализация так и остаётся единственной. Например, библиотека умирает вместе с проектом, платформой etc. Но это НЕ означает автоматически overengineering, ведь фиксировать свои мысли об архитектуре надо по мере их появления, начиная с самой ранней версии. Overengineering'ом это может быть в каждом конкретном случае. Или не быть. Поэтому когда бараны кругом начинают блеять своё «YAGNI», надо требовать доказательств аргументов применительно к данному классу и данному интерфейсу. Общий случай не катит. Хотя в частном они могут оказаться правы.

  Скрытый текст
Как вы понимаете, такой подход обесценивает YAGNI в принципе.


2. В каждом конкретном случае думать приходится творчески, к механическим правилам это свести невозможно. (Невозможно заложить правила проверки в static code analyser). Такой ответ, наверно, кого-то разочарует, но кого-то, наоборот, успокоит: у всех так, посоны.

Б>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.


Зависит мало, на самом деле. 20 лет назад в плюсах дефайнили interface на class/struct и вперёд. Не для компилятора же мы это писали.
Do you want to develop an app?
Отредактировано 08.09.2020 17:04 Shtole . Предыдущая версия .
Re: Интерфейсы и реализация
От: samius Япония http://sams-tricks.blogspot.com
Дата: 08.09.20 18:55
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Чем руководствуетесь, принимая решение выделять или не выделять интерфейс из класса, т.е. нужно ли разделить интерфейс и реализацию?


Б>Когда имеется несколько реализация — понятно, интерфейс обычно выделяют.

Б>Но если реализация только одна, для каких классов выделяете интерфейсы, а для каких нет?

Есть такое соображение, как минимизация кол-ва перестраиваемых модулей/сборок/пакетов. Руководствуясь им (в том числе им), принимаю решение о том, что нужно разделить интерфейс и реализацию даже в случае единой реализации. Интерфейсы кидаются в стабильный пакет, а реализация живет в нестабильном, который подключается динамически. Или даже статически, но уже на этапе внедрения зависимостей.

Так же можно поступать, когда разработку реализации нужно выделить вовне, либо отделить по срокам.

Б>P.S. Возможно, ответ сильно зависит от используемого языка. По возможности, укажите используемый язык.
Re[2]: Интерфейсы и реализация
От: diez_p  
Дата: 21.10.20 14:32
Оценка:
Здравствуйте, СвободуАнжелеДевис, Вы писали:


Б>>Когда имеется несколько реализация — понятно, интерфейс обычно выделяют.

Б>>Но если реализация только одна, для каких классов выделяете интерфейсы, а для каких нет?

САД>Выделяется при любом взаимодействии двух и более классов, ибо тесты.

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

Б>>Но если реализация только одна, для каких классов выделяете интерфейсы, а для каких нет?

Когда обращение должно быть по интерфейсу, вместа типа. Например класс внутренняя реализация модуля (internal) создает этот тип какой-то провайдер и необходимо оставить реализацию типа внутри билиотеки, либо может внутри какого-то namespace (физически нельзя запретить, но соглашением можно)
Re[2]: Интерфейсы и реализация
От: diez_p  
Дата: 21.10.20 14:33
Оценка:
S>.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.
А что вам мешает подсоввать тип напрямую?
Re[3]: Интерфейсы и реализация
От: Sharov Россия  
Дата: 21.10.20 19:49
Оценка:
Здравствуйте, diez_p, Вы писали:

S>>.net core, С#. Стал с недавнего времени использовать DI, а там без интерфейсов практически никуда.

_>А что вам мешает подсоввать тип напрямую?

А зачем тогда DI? А если я конфигурирую реализации в конфиге, и тип узнаю только в runtime?
Кодом людям нужно помогать!
Re[5]: Интерфейсы и реализация
От: scf  
Дата: 24.10.20 07:01
Оценка:
Здравствуйте, diez_p, Вы писали:

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


Две причины.

Интерфейс служит документацией к классу, не загромождая её реализацией, интерфейс позволяет подсунуть другую реализацию в юнит-тестах.
Re[5]: Интерфейсы и реализация
От: Sharov Россия  
Дата: 25.10.20 12:51
Оценка:
Здравствуйте, diez_p, Вы писали:

S>>А зачем тогда DI? А если я конфигурирую реализации в конфиге, и тип узнаю только в runtime?

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

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

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


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

_>Условно есть три сервиса

_>S1, S2, S3 — конкретные типы.

_>Есть клиенты этих сервисов

_>C12(S1, S2), C23(S2, S3), C123(S1, S2, S3)
_>регистрируется все это
_>Di.Register<S1>
_>Di.Register<S2>
_>Di.Register<S3>
_>Di.Register<C12>
_>Di.Register<C23>
_>Di.Register<C123>

_>при вызове DI.Resolve<C123> контейнер создаст S1, S2, S3, а при вызове DI.Resolve<C12>, только S1, S2

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

Если завязываться на DI, то уж лучше использовать интерфейсы и пользоваться всеми плюшками DI. В противном случае и DI не нужен.
Кодом людям нужно помогать!
Re[6]: Интерфейсы и реализация
От: diez_p  
Дата: 27.10.20 14:57
Оценка:
Здравствуйте, Sharov, Вы писали:

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


S>>>А зачем тогда DI? А если я конфигурирую реализации в конфиге, и тип узнаю только в runtime?

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

S>Вероятно я был не так понят, но в конфиге мы указываем реализацию, а не интерфейс. Интерфейс в коде, в конфиге только реализация.


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


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

Вот как поменяются бизнес требования, так и допишите, при этом интерфейс возможно и поменяется.
Это называется излишнее проектирование наперед. Об это много пишут в книжках.
Ну и потом такой подход это сродни инвестированию. Добавить интерфейсы в Java/C# и заменить вызовы делется быстро, нет смысла это делать наперед. Если же делать что-то наперед тяжело изменяемое, то для этого надо иметь весомые обоснования.

Про интерфейсы только для тестирования выше писали и я тоже писал http://rsdn.org/forum/dotnet/7826666.1
Автор: diez_p
Дата: 10.09.20

интерфейс ради тестирования — раздувание контракта. Фреймворки умеют мокать методы классов, если не умеют можно написать велосипед.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.