Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе.
В этом есть какой-то тайный смысл, или они просто идиоты?
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>В этом есть какой-то тайный смысл, или они просто идиоты?
Они делают Как Положено, и чувствуют, как от этого увеличивается их социальный статус, и как им становится можно унижать тех кто не делает.
Здравствуйте, Codealot, Вы писали:
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>В этом есть какой-то тайный смысл, или они просто идиоты?
Так иногда делают чтобы отделить интерфейс от реализации. Представьте, что есть очень жирный и большой класс, и это несмотря на то, что у него всего лишь два публичных метода. А теперь задача — найти среди этих тысяч строк реализации одгого класса чего же от него могут хотеть те, кто его используют.
Это становится похоже на поиск иголок в стоге сена — и по времени и по ментальным трудозатратам. Вот тогда иногда и делают отдельный интерфейс, который строго завляет — нужно только 2 таких вот метода.
А тысячи строк реализации — это уже дело второстепенное и совершенно неинтересное, особенно если над проектом работает несколько людей со своими зонами ответственности. Им нырять в чужие тысячи строк очень больно, но беглым взглядом увидеть суть в коротком определении интерфейса — очень легко и понятно.
Это классика подхода под названием "сервисо-ориентированая архитектура".
Здравствуйте, Codealot, Вы писали:
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>В этом есть какой-то тайный смысл, или они просто идиоты?
Ну как же. Это для юнит-тестов делается, чтобы моки писать.
Здравствуйте, Codealot, Вы писали:
С>>Ну как же. Это для юнит-тестов делается, чтобы моки писать.
C>Так нет там никаких моков. Да и никаких тестов практически нет
Здравствуйте, Codealot, Вы писали:
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>В этом есть какой-то тайный смысл, или они просто идиоты?
Здравствуйте, AlexGin, Вы писали:
AG>Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее. AG>Кроме того, имеется заложенная база — как для развития (новые типы узлов), AG>так и для тестирования основы нашего проекта.
не просто "красивее", но и безопасней
1. контракт зафиксирован == клиенты могут начать разработку до того как будет закончена имплементация. клиенты могут писать юнит-тесты мокируя объект
2. имплементация спрятана от клиентов, что не позволяет особо продвинутым все сломать залазя туда своими кривыми руками
3. никто от балды не может добавить "полезный" метод в интерфейс
4. можно заменить реализацию и использовать две параллельно
5. интерфейс вообще можно отделить в отдельную либу и отправить на сторону клиента(например если мы пишем сетевые сервисы)
Здравствуйте, Codealot, Вы писали:
C>Каких?
Intersection types, например, да и Union Types бы не помешали, с возможностями по ним матчиться и с автоматической проверкой вариантов на полноту при компиляции и даже раньше. list comprehension тоже не хватает, я конечно умею это все делать через стримы и map, но это получается через задницу и не красиво. Полноценные tuples с удобной деструктуризацией и тому подобное. В среде разработки не хватает возможности получить список всех ошибок и проблем всего проекта отдельной вкладке problems (сейчас есть такое только по одному текущему файлу, а я привык что такое возможно по всему проекту). Не хватает возможности перехода по val или var на соответствующий тип при нажатии ctrl и мышкой на нем в IDEA (в свое время я был инициатором этого feature request). Это навскидку, я уж забыл чего мне не хватает .
Здравствуйте, scf, Вы писали:
C>>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>>В этом есть какой-то тайный смысл, или они просто идиоты?
scf>От языка зависит. В Джаве это выглядит действительно странно, но если язык позволяет объявить интерфейс и реализацию в одном классе, то выделенный интерфейс — это хорошая практика.
Что за наезды на джаву?
public interface Calculator {
int add(int a, int b);
static Calculator create() {
return new Impl();
}
class Impl implements Calculator {
@Override
public int add(int a, int b) {
return 0;
}
}
}
Здравствуйте, Aquilaware, Вы писали:
A>Так иногда делают чтобы отделить интерфейс от реализации. Представьте, что есть очень жирный и большой класс, и это несмотря на то, что у него всего лишь два публичных метода. А теперь задача — найти среди этих тысяч строк реализации одгого класса чего же от него могут хотеть те, кто его используют.
Публичные методы — вверх. Всё.
И это все равно не тот случай.
Здравствуйте, AlexGin, Вы писали:
AG>Здесь и у Вас, и у меня — две сушности.
ну никто не мешает сделать одну:
class node
{
std::vector<std::shared_ptr<node>> childs;
};
AG>Но у меня — универсальный узел и интерфейс к нему.
Нет, у тебя узел и иузел.
AG>Именно наличие программного интерфейса — позволяет уменьшить связанность (coupling) в проекте.
А конкретно в данном случае что именно уменьшается?
AG>За счёт этого, получим упрощение архитектуры и уменьшение количества ошибок.
Введение новой сущности интерфейса не упрощает, а усложняет.
AG>Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее.
Эмоции. "Я так всегда делал и мне нравится"
AG>Кроме того, имеется заложенная база — как для развития (новые типы узлов),
yagni. Вот _если_ появятся новые узлы — вот тогда и добавим интерфейс. Впрочем, тут тоже могу условно согласиться. Если средство разработки не умеет в рефакторинг "выделить интерфейс", то приходится прыгать... впрочем и без авто-тулзов ввести интерфейс _при необходимости_ относительно просто. Проще, чем постоянно копаться в сотнях интерфейсов, созданных на всякий случай.
AG>так и для тестирования основы нашего проекта.
Вот тут могу согласиться для некоторых ЯП. Если ЯП не позволяет тестировать без интерфейсов, то их приходится вводить на каждый чих.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Aquilaware, Вы писали:
A>Так иногда делают чтобы отделить интерфейс от реализации. Представьте, что есть очень жирный и большой класс, и это несмотря на то, что у него всего лишь два публичных метода. А теперь задача — найти среди этих тысяч строк реализации одгого класса чего же от него могут хотеть те, кто его используют. A>Это становится похоже на поиск иголок в стоге сена — и по времени и по ментальным трудозатратам. Вот тогда иногда и делают отдельный интерфейс, который строго завляет — нужно только 2 таких вот метода.
Угу. А через несколько лет захочется добавить классу публичный метод. И вот тут становится интересно. Что, если это разработчики-затейники сделали еще 5 реализаций этого интерфейса? Во всех них придется реализовывать это метод. Ровно ту же самую проблему решает абстрактный класс с конструктором пакетной видимости (не protected и не public). В результате на грабли интерфейса наступить просто не получится. Да и метод можно будет при необходимости легко добавить.
Там же (в Practical API Design: Confessions of a Java Framework Architect) вообще рекомендуется использовать классы (абстрактные и нет) для функциональности, предоставляемой (provided) библиотекой. А интерфейсы — для функциональности, требуемой (required) библиотекой. Как обычно, исключения — всякие коллекции и прочее "API общего пользования".
Здравствуйте, Codealot, Вы писали:
C>Публичные методы — вверх. Всё. C>И это все равно не тот случай.
Различать всё-таки следует понятия: класс и модуль:
Класс — может выступать элементом внутренней структуры.
Его публичные методы — вполне возможно это внутренние шестерёнки,
оставленные открытыми только для работы с объектами этого класса его "начальниками" (клиентами).
Никто другой не работает с этим классом. Большинству окружающих даже не важно, что эти методы public.
В терминологии C++ есть понятие friend. Иногда я даже пользуюсь этим, нарушая принцыпы ООД.
То есть: публичные методы класса — не факт, что являются входами/выходами всго модуля (или даже подсистемы).
Чего уже НЕ СКАЖЕМ об интерфейсе: здесь методы именно определяющие взаимодействие модуля с внешним миром.
Они обязаны быть максимально независимыми от внутренней реализации. Простой "публичностью" здесь уже не обойтись.
P.S. Вот поэтому, реализация (даже если и единичная) нашего модуля ИМХО заслуживает наличия собственного интерфейса.
Это, казалось бы, усложнение — делает понимание кодов проекта проще и быстрее. Оно экономит силы и время (в том числе даже и
самому автору разработки, когда по воле случая он раскроет свои же коды 5-ти летней давности).
Здравствуйте, Codealot, Вы писали:
AG>Его публичные методы — вполне возможно это внутренние шестерёнки,
C>Значит, они не должны быть публичными. Всё.
Да, именно, внутренние — для модуля.
Они могут выходить за пределы класса. Могут быть публичными для класса.
C>Вот так, не умея использовать базовые конструкции языка, люди придумывают какую-то хрень.
Вот так, люди не писавшие ничего сложнее "Hello world!", пытаются навязать своё видиние единственно правильной архитектуры ПО.
Здравствуйте, Codealot, Вы писали:
Y>>Имелось в виду, что будут. Но потом. Но не срослось.
C>YAGNI. Если понадобится — тогда и надо добавлять.
потом не получится, правильно надо делать сразу.
пример, работал я над проектом в котором повсюду встречался код:
... // код какого-то метода
someObj.setField(SomeConfig.getInstance().getFieldValue())
... // код продолжается
и все, пока в mockito не появился mockStatic никаких зубодробительных юнит-тестов не напишешь. а если это не java, то возможно вообще не напишешь
EDITED:
тоже относится к методам которые, например вычисляют даты и время у себя внутри. ты их не протестируешь потому как значения всегда будут разные
Здравствуйте, baxton_ulf, Вы писали:
_>пример, работал я над проектом в котором повсюду встречался код: _>
_> ... // код какого-то метода
_> someObj.setField(SomeConfig.getInstance().getFieldValue())
_> ... // код продолжается
_>
_>и все, пока в mockito не появился mockStatic никаких зубодробительных юнит-тестов не напишешь. а если это не java, то возможно вообще не напишешь
Элементарно рефакторится.
Начнем с того, что SomeConfig.getInstance().getFieldValue() — это лютый копипаст и так делать нельзя и от такого нужно избавляться мгновенно на автомате как только увидел такое.
Ну и переписывается это на что то вроде
someObj.setField(ConfigHelper.getFieldValue())
,
причем можно еще и static import сделать для читаемости.
ConfigHelper уже пишется таким образом, что уже достаточно легко можно определять с каким конфигом работать, тестовым или нет, причем переопределять все в рантайме. Непосредственно в тестах быстро пишется утилита, которая позволяет работать с разными конфигами.
И все — все дополнительные сложности локализованы в одном месте, очень быстрым легким и безопасным рефакторингом, весь остальной код именно что жестко перелопачивать не требуется. Уже утилитные Helper классы пишутся с нуля, там могут быть любые навороты.
А далее уже можно подумать над использованием фреймворков, где уже есть всякие DI в зависимости от контекста, где уже задача нормального тестирования, мокирования, подмены конфигов и т.д работает из коробки и т.д. Если основной код требуется сделать независим от таких фреймворков — ок, хорошо, просто используем такие фреймворки только в тестах, делая мостик между тем, как положено там тестировать и нашими костылями с подменой конфига.
Здравствуйте, ·, Вы писали:
·>Кстати, появились sealed classes, но честно говоря пока не придумал когда их надо использовать на практике.
Так каноничный пример — стейт машина и в зависимости от состояния там разные значения, и это как альтернатива енумам. Условно говоря — состояние в очереди, там дата постановки в очередь, состояние в работе — там добавляется дата запуска и логи выполнения, состояние завершено, там есть дата завершения, логи, результат и дата старта, состояние ошибка с логами, датой ошибки, причиной ошибка. Очень удобно и красиво. Там где енум напрагмвается, сразу имеет смысл подумать над sealed классами, возможно имеет смысл.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Осознал проблему?
Так нет никакой проблемы. Если пишется именно утилита для повторного использования, то будешь изначально предполагать, что пользователь захочет много чего интересного и статический конфиг не подойдет.
Если изначально писалась простенькая утилитка для скриптинга на нормальном языке, то по умолчанию будет статический конфиг, возможно даже хардкодом для скорости. Если же вдруг задача оказывается типичной и захочется именно повторного использования — это все очень быстро рефакторится, и уже будет и класс, и к классу привязан конфиг в конструкторе плюс там будут параметры по умолчанию — никакой проблемы, рефакторится все очень быстро.
M>Угу. А через несколько лет захочется добавить классу публичный метод. И вот тут становится интересно. Что, если это разработчики-затейники сделали еще 5 реализаций этого интерфейса? Во всех них придется реализовывать это метод. Ровно ту же самую проблему решает абстрактный класс с конструктором пакетной видимости (не protected и не public). В результате на грабли интерфейса наступить просто не получится. Да и метод можно будет при необходимости легко добавить.
чего? ты понимаешь, что интерфейс это контракт? его не меняют (очень редко и то если изменение аддитивно и не ломает обратную совместимость). если надо добавить метод, то создают новый интерфейс. интерфейс позволяет отвязаться от реализации — это никакой абстрактный класс не решит. а абстрактные классы позволяют избежать дублирования кода в реализациях.
и да не все классы должны иметь выделенный интерфейс,
и да надо хорошо думать прежде чем создавать интерфейс.
Здравствуйте, Codealot, Вы писали:
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>В этом есть какой-то тайный смысл, или они просто идиоты?
От языка зависит. В Джаве это выглядит действительно странно, но если язык позволяет объявить интерфейс и реализацию в одном классе, то выделенный интерфейс — это хорошая практика.
В этом случае файл становится модулем, состоящим из интерфейса-контракта, описывающего его публичное API, документированного, не загроможденного реализацией, и реализации, изолированной от контракта.
Экземпляр такого интерфейса получают через фабричный метод, т.к. а) это завершает изоляцию интерфейса от реализации б) частенько бывает, что при создании экземпляра нужна какая-то асинхронная инициализация, а конструкторы всегда синхронные в) вынос кода инициализации и подготовки данных из конструктора в фабричный метод позволяет оформить реализацию иммутабельным классом, получающим все свои зависимости через конструктор.
Здравствуйте, Codealot, Вы писали:
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе.
Индусы? Может количество кода увеличивают? C>В этом есть какой-то тайный смысл
Это болезнь какая-то. Тоже столкнулся в работе с таким подходом где человек просто на всё создаёт интерфейс чтобы отвязаться от реализации или подсунуть МОК, правда у нас нет ни одного юнит теста и интерфейсы не используются, по сути, никогда.
Здравствуйте, Codealot, Вы писали:
C>Здравствуйте, scf, Вы писали:
scf>>От языка зависит.
C>C#
я на котлине такое видел. Простенькая программы содержала в себе тысячи этих классов, интерфейсов и фабрик. Я нужный мне кусок кода (протокол работы с БТ) даже поиском по ключевым словам найти не сразу смог, всё упирался в какие-то пустые интерфейсы. А ведь это мобила, ресурсы стоило бы и поберечь, наверное
Здравствуйте, wl., Вы писали:
wl.>Я нужный мне кусок кода (протокол работы с БТ) даже поиском по ключевым словам найти не сразу смог, всё упирался в какие-то пустые интерфейсы.
Тут примерно такая же фигня. По функционалу — всего лишь обертка над другой библиотекой.
Здравствуйте, scf, Вы писали:
vsb>>Что за наезды на джаву?
scf>Вот так, спустя 20 лет программирования на джаве, узнаешь, что можно в интерфейсах объявлять вложенные классы.
Не ручаюсь, что 20 лет назад это работало, возможно это в какой-то из последних версий добавили. private class почему-то нельзя так объявить...
Здравствуйте, Codealot, Вы писали:
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>В этом есть какой-то тайный смысл, или они просто идиоты?
В конторе, где я сейчас работаю, прямо в правилах кодирования есть пункт запрещающий создавать интерфейс если его реализация будет ровно в одном классе. Причин не знаю.
Здравствуйте, B0FEE664, Вы писали:
BFE>В конторе, где я сейчас работаю, прямо в правилах кодирования есть пункт запрещающий создавать интерфейс если его реализация будет ровно в одном классе. Причин не знаю.
1) Если в качестве борьбы с перекрёстными ссылками?
2) Для представления древовидных структур, с коллекциями элементов того же типа, что и корневой элемент?
Когда в корневом элементе требуется иметь коллекцию child-элементов.
как тогда быть?
P.S. Подразумеваю C++ и наличие файлов *.h & *.cpp
Здравствуйте, Codealot, Вы писали:
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>В этом есть какой-то тайный смысл, или они просто идиоты?
AG>1) Если в качестве борьбы с перекрёстными ссылками? AG>2) Для представления древовидных структур, с коллекциями элементов того же типа, что и корневой элемент? AG>Когда в корневом элементе требуется иметь коллекцию child-элементов. AG>как тогда быть? AG>P.S. Подразумеваю C++ и наличие файлов *.h & *.cpp
Не вижу препятствий.
class child;
class parent
{
std::vector<std::shared_ptr<child>> childs;
};
Здравствуйте, уважаемый B0FEE664, Вы писали:
AG>Для представления древовидных структур, с коллекциями элементов того же типа, что и корневой элемент? AG>Когда в корневом элементе требуется иметь коллекцию child-элементов.
BFE>Не вижу препятствий.
BFE>
Элемент дерева, как логично я заметил выше, один и тот же.
Для каких-то других элементов — он является parent-ом.
Для других — чайлдом.
Ссылочку на этот чайлд хранит родитель (parent) в своей коллекции.
То, что Вы писали — это два разных класса.
Я бы предложил так:
class Node : public INode // Класс реализации узла (header):
{
public:
Node(const std::string& sNameOfNode); // C-tor
~Node(); // D-tor
std::string GetNameOfNode() const override;
std::vector<std::shared_ptr<INode>> GetVectOfChilds() const override;
void AddNode(std::shared_ptr<INode> p) override;
};
Здесь и у Вас, и у меня — две сушности.
Но у меня — универсальный узел и интерфейс к нему.
Именно наличие программного интерфейса — позволяет уменьшить связанность (coupling) в проекте.
За счёт этого, получим упрощение архитектуры и уменьшение количества ошибок.
Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее.
Кроме того, имеется заложенная база — как для развития (новые типы узлов),
так и для тестирования основы нашего проекта.
Поковырявшись еще в проекте, нашел немало перлов типа такого:
if (text.ToUpper().Contains(...))
doSomething();
else if (text.ToUpper().Contains(...))
doSomethingElse();
else if (text.ToUpper().Contains(...))
doSomethingDifferentElse();
Так что вопрос о качестве кода можно закрывать. Джуниор, которого покусал Гамма.
Всех, пто придумывал оправдания такому коду — поздравляю.
Здравствуйте, AlexGin, Вы писали:
AG>Да, именно, внутренние — для модуля.
У тебя какое-то странное понимание модуля. Как и всего остального.
AG>Вот так, люди не писавшие ничего сложнее "Hello world!", пытаются навязать своё видиние единственно правильной архитектуры ПО.
Пальцем в небо. Очень сильно сомневаюсь, что у тебя есть хотя бы малейшие реальные основания для таких понтов.
Здравствуйте, AlexGin, Вы писали:
AG>Чего уже НЕ СКАЖЕМ об интерфейсе: здесь методы именно определяющие взаимодействие модуля с внешним миром.
Это прост какие-то соглашения. Раз интерфейс, давайте его считать чем-то особым и назовём "интерфейсом модуля". Врочем, это опять всё от конкретного ЯП зависит. В плюсах там с модульностью вообще плохо. Вот и приходится опираться на определённые соглашения.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
AG>>Но у меня — универсальный узел и интерфейс к нему. ·>Нет, у тебя узел и иузел.
Напомню, что в терминологии C++ (в отличие от C# и Java) понятие интерфейс отсутствует.
Вместо него обычно применяется: абстрактный_класс: https://www.tutorialspoint.com/cplusplus/cpp_interfaces.htm
AG>>Именно наличие программного интерфейса — позволяет уменьшить связанность (coupling) в проекте. ·>А конкретно в данном случае что именно уменьшается?
Показана идея применения абстрактного_класса C++ в качестве интерфейса.
Здесь (в приведенном примере) всё просто, поэтому избыточной связанности нет.
AG>>За счёт этого, получим упрощение архитектуры и уменьшение количества ошибок. ·>Введение новой сущности интерфейса не упрощает, а усложняет.
Конкретно здесь — упрощает понимание (даже если незначительно и увеличивает объём кода).
AG>>Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее. ·>Эмоции. "Я так всегда делал и мне нравится"
Если быть точным, то делал я так не всегда.
Первые лет пять/шесть разработки на C++ я практически не применял абстрактные классы.
Но примерно с 2015-го понял, что такой подход упрощает понимание проекта.
Даже мне самому легче понять то, что я разработал несколько лет назад.
В литературе это именуют: best practices.
P.S. Мы не ждём, когда спроектируются новые узлы поезда/вагоны, а подготавливаем проект "рельсового пути" для существующих и новых поездов.
Вы же предлагаете проектировать новые рельсы, как только появятся новые вагоны.
YAGNI — если бы я предлагал проектировать поезд на магнитной подушке, для поездки по линии длиной 5 км, где вполне хватит обычного трамвая.
Здравствуйте, elmal, Вы писали:
E>Начнем с того, что SomeConfig.getInstance().getFieldValue() — это лютый копипаст и так делать нельзя и от такого нужно избавляться мгновенно на автомате как только увидел такое. E>Ну и переписывается это на что то вроде E>
, E>причем можно еще и static import сделать для читаемости. E>ConfigHelper уже пишется таким образом, что уже достаточно легко можно определять с каким конфигом работать, тестовым или нет, причем переопределять все в рантайме. Непосредственно в тестах быстро пишется утилита, которая позволяет работать с разными конфигами.
А что, getInstance() не даст переопределить конфиг на любой тестовый?
Кроме чуть более длинной записи (и то можно результат getInstance() закэшировать в пределах метода) — проблемы не видно.
А делать на каждое поле отдельный метод ещё и в ConfigHelper — задолбёшься.
Хотя если это ConfigHelper().getFooValue() — то ещё более-менее норм.
E>А далее уже можно подумать над использованием фреймворков, где уже есть всякие DI в зависимости от контекста, где уже задача нормального тестирования, мокирования, подмены конфигов и т.д работает из коробки и т.д. Если основной код требуется сделать независим от таких фреймворков — ок, хорошо, просто используем такие фреймворки только в тестах, делая мостик между тем, как положено там тестировать и нашими костылями с подменой конфига.
Ну так подменять объект конфигурации для теста — очевидность.
Здравствуйте, B0FEE664, Вы писали:
BFE>В конторе, где я сейчас работаю, прямо в правилах кодирования есть пункт запрещающий создавать интерфейс если его реализация будет ровно в одном классе. Причин не знаю.
Причина проста. Кода должно быть ровно столько, сколько необходимо. Минимальное количество кода — имеем одну реализацию. Потребовалась другая — выделяем интерфейс. Выделяем когда потребуется, а не потому, что так "положено".
Меньше кода — это легче читается, легче читается — проще понимание, проще понимание — делаешь меньше ошибок, делаешь меньше ошибок — задача быстрее будет сдана. Меньше кода — это более легкие и более быстрые изменения, это меньше проблемами с мержем с другими и т.д. При развитии можно все сложности во всякие библиотеки и утилиты добавлять, а основной код должен быть минимальным по объему. А библиотеки и утилиты — там вообще никаких классов, там тупо набор легко тестируемых функций.
Здравствуйте, netch80, Вы писали:
N>Хотя если это ConfigHelper().getFooValue() — то ещё более-менее норм.
Вот вообще не понимаю эту любовь к оператору new. Ну блин на хрена на ровном месте создавать объект чтобы там дернуть метод?
Конфиг он на то и конфиг, что он один. Если всякие DI фреймворки не используешь — сделай его глобальным и не мучайся, опиши только это в документации. Ничего страшного не случится если в проекте будет одна глобальная переменная вроде конфига, о которой все знают и правила доступа к которой известны и соблюдаются. Альтернативы в виде доступ через синглтоны всякие и фабрики со всякими копипастами — это по сути тоже глобальная переменная, вот только кода сильно больше получается и работает все гораздо запутаннее. А если используешь DI фреймворки — там все достаточно удобно все сделано, наработаны давно бест практики, кода будет абсолютно минимум и все удобно — живи да радуйся что у тебя все чистенько и по фен шую.
Здравствуйте, elmal, Вы писали:
E>Здравствуйте, netch80, Вы писали:
N>>Хотя если это ConfigHelper().getFooValue() — то ещё более-менее норм. E>Вот вообще не понимаю эту любовь к оператору new. Ну блин на хрена на ровном месте создавать объект чтобы там дернуть метод?
Кто сказал, что там будет new? Сами придумали? Я такого не говорил.
Вполне возможно, что это уже метод чтения синглтона.
(Хотя если пример был строго для Java, то да, возвращаемся к import static.)
E>Конфиг он на то и конфиг, что он один.
Кто сказал? Конфиг это контекст. Контекстов может быть несколько. Например, несколько экземпляров стека протоколов на разных IP адресах или портах, с разными свойствами.
(Если вы собрались перевалить это на ОС, деля конфиги по её процессам, или вообще по контейнерам, то там то же самое, но замаскировано переключением контекста в другом месте.)
А ещё контекст может меняться при входе в какую-то функцию и возвращаться при выходе. Тогда он где-то в thread local data. А если акторы на асинке, то ещё и его замыкает в свойствах актора.
E> Если всякие DI фреймворки не используешь — сделай его глобальным и не мучайся, опиши только это в документации. Ничего страшного не случится если в проекте будет одна глобальная переменная вроде конфига, о которой все знают и правила доступа к которой известны и соблюдаются. Альтернативы в виде доступ через синглтоны всякие и фабрики со всякими копипастами — это по сути тоже глобальная переменная, вот только кода сильно больше получается и работает все гораздо запутаннее.
Про фабрики я не говорил. Синглтон в условиях современных языков это метод обеспечить защиту от изменения выбора объекта, где не надо. getInstance() всем, setInstance(), если есть, только тем, кому положено.
E> А если используешь DI фреймворки — там все достаточно удобно все сделано, наработаны давно бест практики, кода будет абсолютно минимум и все удобно — живи да радуйся что у тебя все чистенько и по фен шую.
Они позволяют сделать временную замену конфига на один вызов и пробросить это в отложенный коллбэк?
Здравствуйте, netch80, Вы писали:
N>Кто сказал? Конфиг это контекст. Контекстов может быть несколько. Например, несколько экземпляров стека протоколов на разных IP адресах или портах, с разными свойствами.
Я вообще то считал, что конфиг — это такое условно неизменное что то, а контекст — это что то постоянно меняющееся, соответственно это все совершенно разные вещи.
N>Они позволяют сделать временную замену конфига на один вызов и пробросить это в отложенный коллбэк?
Если потребовалось такое, то мы имеем не конфиг, а контекст чего то. Контекст привязан к чему то, например к текущему потоку, к пользователю, ну или к объекту, например к Http запросу. И да, фреймворки позволяют для конкретного HTTP запроса иметь свой контекст, в который можем класть что хотим и получать что хотим.
Здравствуйте, AlexGin, Вы писали:
AG>>>Но у меня — универсальный узел и интерфейс к нему. AG>·>Нет, у тебя узел и иузел. AG>Напомню, что в терминологии C++ (в отличие от C# и Java) понятие интерфейс отсутствует. AG>Вместо него обычно применяется: абстрактный_класс: AG>https://www.tutorialspoint.com/cplusplus/cpp_interfaces.htm
Не знаю зачем ты это всё рассказал, но это я и так прекрасно знаю. Если этот код переписать на C#/Javа суть не изменится.
Даже хуже. В плюсах и так уже есть пара h/cpp, а ты ещё и третью штутку создаёшь.
AG>>>Именно наличие программного интерфейса — позволяет уменьшить связанность (coupling) в проекте. AG>·>А конкретно в данном случае что именно уменьшается? AG>Показана идея применения абстрактного_класса C++ в качестве интерфейса.
Замечательно, а в киеве дядька.
AG>Здесь (в приведенном примере) всё просто, поэтому избыточной связанности нет.
Ок. Т.е. и пользы от интерфейса (в приведённом примере) тоже нет (по крайней мере для устранения избыточной связности). ЧТД.
AG>>>За счёт этого, получим упрощение архитектуры и уменьшение количества ошибок. AG>·>Введение новой сущности интерфейса не упрощает, а усложняет. AG>Конкретно здесь — упрощает понимание (даже если незначительно и увеличивает объём кода).
Это субъективно. Просто делает код более привычным тебе лично. А объективное усложнение хотя бы по объёму кода точно есть. Плюс объективно появляется лишняя сущность в коде, которая требует поддержки.
AG>>>Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее. AG>·>Эмоции. "Я так всегда делал и мне нравится" AG>Если быть точным, то делал я так не всегда. AG>Первые лет пять/шесть разработки на C++ я практически не применял абстрактные классы. AG>Но примерно с 2015-го понял, что такой подход упрощает понимание проекта. AG>Даже мне самому легче понять то, что я разработал несколько лет назад.
Да. Я тоже таким был. А потом ещё пять-шесть лет опыта, научился пользоваться современными тулзами и начал понимать что надо _не_ делать.
AG>В литературе это именуют: best practices.
Не знаю конкретно какой код ты имеешь в виду, но в приведённом примере это именуется cargo cult.
AG>P.S. Мы не ждём, когда спроектируются новые узлы поезда/вагоны, а подготавливаем проект "рельсового пути" для существующих и новых поездов. AG>Вы же предлагаете проектировать новые рельсы, как только появятся новые вагоны. AG>YAGNI — если бы я предлагал проектировать поезд на магнитной подушке, для поездки по линии длиной 5 км, где вполне хватит обычного трамвая.
Ты приводишь аналогию из реального мира, где всё дорого и требует серьёзного планирования и распространяешь это на софтостроение, где изменения могут быть настолько дешевыми, что и не стоит задумываться.
Представь себе, что пустить поезд поезд на магнитной подушке или трамвай будет занимать 2 минуты — будет ли тебя заботить этот вопрос? Бросишь то, что попадётся под руку, поезд там, или самолёт, а если узнаешь, что всего 5км, то за минуту заменишь на трамвай.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, elmal, Вы писали:
N>>Кто сказал? Конфиг это контекст. Контекстов может быть несколько. Например, несколько экземпляров стека протоколов на разных IP адресах или портах, с разными свойствами. E>Я вообще то считал, что конфиг — это такое условно неизменное что то, а контекст — это что то постоянно меняющееся, соответственно это все совершенно разные вещи.
Так нету здесь точной границы. Вот я зову GetServerAddress(). Это конфигурация? Очевидно, да. Оно раздельно в зависимости от того, какой из экземпляров стека протоколов нужно дёрнуть? Да.
Ну и как мне их сделать "разными вещами"?
Конфигурировать их, вероятно, можно тоже раздельно. А в случае Cisco style (частый в моём мире) скорее всего так и будет — подправил через CLI параметр в секции, изменился только он, пошла нотификация куда-то вглубь.
N>>Они позволяют сделать временную замену конфига на один вызов и пробросить это в отложенный коллбэк? E>Если потребовалось такое, то мы имеем не конфиг, а контекст чего то. Контекст привязан к чему то, например к текущему потоку, к пользователю, ну или к объекту, например к Http запросу. И да, фреймворки позволяют для конкретного HTTP запроса иметь свой контекст, в который можем класть что хотим и получать что хотим.
Ну в типичном случае таки да. Для теста (если это не тест собственно механизма контекста) обычно не нужно делать в одном процессе одновременно, или вложенно по стеку, замену конфигурации.
Хотя в функциональные тесты может входить и одновременный запуск нескольких экземпляров контекста-конфигурации и взаимодействие между ними. (Хм, дефис тут удобен.)
Здравствуйте, AlexGin, Вы писали:
AG>·>Не знаю зачем ты это всё рассказал, но это я и так прекрасно знаю. AG>Очень надеюсь, что кроме нас на КЫВТ есть ещё посетители, которым это будет интересно. AG>Не стоит думать, что все полезные советы люди смотрят на SO.
Это называется оффтопик. Т.е. к тезису что твой пример был примером бессмысленного интерфейса возражений не осталось?
AG>... AG>·>Да. Я тоже таким был. А потом ещё пять-шесть лет опыта, научился пользоваться современными тулзами и начал понимать что надо _не_ делать. AG>Какими именно тулзами? Что делают эти тулзы?
Рефакторинг, анализ кода, навигация, генерация документации по коду, моки поверх классов, етс.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Это называется оффтопик. Т.е. к тезису что твой пример был примером бессмысленного интерфейса возражений не осталось?
Бессмысленность или наличие смысла — всегда зависит от контекста задачи.
Если я пояснил на примере пользу от интерфейса, пусть даже ценой небольшого увеличения объема кода,
это совсем не означает, что _каждый_класс_ в моих проектах имеет интерфейс
Имеют только те, которые по логике проекта (по логике архитектурных решений) должны его иметь.
Бессмысленным его наличие считать не следует.
·>Рефакторинг, анализ кода, навигация, генерация документации по коду, моки поверх классов, етс.
Это всё известные всем давно средства.
Надеялся найти в данной ветке что-то новое, но не судьба
Здравствуйте, AlexGin, Вы писали:
AG>·>Это называется оффтопик. Т.е. к тезису что твой пример был примером бессмысленного интерфейса возражений не осталось? AG> AG>Бессмысленность или наличие смысла — всегда зависит от контекста задачи.
Контекст обозначен явно в стартовом сообщении "каждый интерфейс реализован ровно в одном классе." По моему тезису в этом контексте интерфейсы бессмысленны. Ты с этим согласен или нет?
AG>Если я пояснил на примере пользу от интерфейса, пусть даже ценой небольшого увеличения объема кода,
Я не увидел пояснений, кроме как "мне понятнее". Ещё ты что-то написал про "уменьшить связанность", но потом ты согласился что тут оно не в тему.
AG>это совсем не означает, что _каждый_класс_ в моих проектах имеет интерфейс
И на том спасибо.
AG>Имеют только те, которые по логике проекта (по логике архитектурных решений) должны его иметь. AG>Бессмысленным его наличие считать не следует.
По умолчанию следует считать всё бессмысленным. Пока нет явного конкретного объективного смысла.
AG>·>Рефакторинг, анализ кода, навигация, генерация документации по коду, моки поверх классов, етс. AG>Это всё известные всем давно средства. AG>Надеялся найти в данной ветке что-то новое, но не судьба
Вот простая новая для тебя мысль: интерфейс (как и любую другую сущность) надо вводить только когда без него нельзя обойтись.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, elmal, Вы писали:
N>>Хотя если это ConfigHelper().getFooValue() — то ещё более-менее норм. E>Вот вообще не понимаю эту любовь к оператору new. Ну блин на хрена на ровном месте создавать объект чтобы там дернуть метод? E>Конфиг он на то и конфиг, что он один. Если всякие DI фреймворки не используешь — сделай его глобальным и не мучайся, опиши только это в документации. Ничего страшного не случится если в проекте будет одна глобальная переменная вроде конфига, о которой все знают и правила доступа к которой известны и соблюдаются.
Не надо глобальные переменные использовать.
E> Альтернативы в виде доступ через синглтоны всякие и фабрики со всякими копипастами — это по сути тоже глобальная переменная, вот только кода сильно больше получается и работает все гораздо запутаннее. А если используешь DI фреймворки...
А бы почему через конструктор не передать? Слишком просто что-ли?.. не солидно?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>А бы почему через конструктор не передать? Слишком просто что-ли?.. не солидно?
Не всегда нужны классы, иногда для простоты и удобства гораздо проще до черта логики сделать обычными top level функциями. Во вторых, если классы все таки нужны, без DI фреймворков ты задолбаешься этот конфиг в каждый класс прокидывать и инициализировать. В случае использования фреймворков все понятно как мы поступаем. Если мы по какой то причине навороты не используем, ничего страшного если на весь проект будет одна глобальная переменная. Достаточно уважаемые люди такое, если что, делают иногда, например Gaving King в языке Ceylon предлагал логирование делать через глобальную переменную, ничего страшного не случалось на проектах в миллионы строк если что.
Здравствуйте, Codealot, Вы писали:
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>В этом есть какой-то тайный смысл, или они просто идиоты?
Чего ты такой злой. Люди любой идиотизм могут делать из самых лучших побуждений.
Возможно их просто так где-то научили так делать, но не сказали, что так нужно делать не всегда.
Здравствуйте, netch80, Вы писали:
N>А в чём проблема — только в некэшировании результата ToUpper()?
А, всего-то навсего накопипастил код?
N>Я не придумывал, но заранее спасибо. Ты ж не показывал. А требование интерфейсов и оптимальность — почти не связаны.
Умение писать адекватный код их связывает. И это даже не оптимальность, это просто азы качественного кода.
Здравствуйте, bnk, Вы писали:
bnk>Чего ты такой злой.
Жизнь такая.
bnk>Люди любой идиотизм могут делать из самых лучших побуждений. bnk>Возможно их просто так где-то научили так делать, но не сказали, что так нужно делать не всегда.
Здравствуйте, Codealot, Вы писали:
bnk>>Люди любой идиотизм могут делать из самых лучших побуждений. bnk>>Возможно их просто так где-то научили так делать, но не сказали, что так нужно делать не всегда.
C>Знаешь, куда вымощена дорога благими намерениями?
Хочешь взять на себя ответственность за этот проект и блюсти его чистоту?
Ну что такого-то, просто лишние интерфейсы и фабрики, не конец света. С этим можно работать.
Может и правда когда-нибудь будут тесты. Пусть безобразно, зато однообразно. Так что все норм.
_>>и все, пока в mockito не появился mockStatic никаких зубодробительных юнит-тестов не напишешь. а если это не java, то возможно вообще не напишешь E>Элементарно рефакторится.
да я что спорю что-ли? все правильно, все рефакторится. но если бы тесты писались вместе с кодом то рефакторить не пришлось бы.
у меня там получилось так — надо было сделать "супер быстрое изменение в одну строчку" и предел по покрытию уже выставили на 80% (что в общем то правильно).
в общем рефакторить пришлось мне и количество изменений и время работы выросли
Здравствуйте, elmal, Вы писали:
E>·>А бы почему через конструктор не передать? Слишком просто что-ли?.. не солидно? E>Не всегда нужны классы, иногда для простоты и удобства гораздо проще до черта логики сделать обычными top level функциями.
Функции неявно зависящие от глобальных переменных?! Вот и передавай им serverAddress как параметр, а не извлекай из глобальных синглтонов.
E>Во вторых, если классы все таки нужны, без DI фреймворков ты задолбаешься этот конфиг в каждый класс прокидывать и инициализировать.
У тебя и вправду каждый класс зависит от serverAddress?!
Просто передавай ровно то что нужно и задалбливаться не придётся.
E>В случае использования фреймворков все понятно как мы поступаем.
Да, фреймворки в топку.
E>Если мы по какой то причине навороты не используем, ничего страшного если на весь проект будет одна глобальная переменная. Достаточно уважаемые люди такое, если что, делают иногда, например Gaving King в языке Ceylon предлагал логирование делать через глобальную переменную, ничего страшного не случалось на проектах в миллионы строк если что.
"Ceylon Stable release 1.3.3 / August 21, 2017; 4 years ago", ну да, ничего страшного.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
E>>Во вторых, если классы все таки нужны, без DI фреймворков ты задолбаешься этот конфиг в каждый класс прокидывать и инициализировать. ·>У тебя и вправду каждый класс зависит от serverAddress?! ·>Просто передавай ровно то что нужно и задалбливаться не придётся.
Начнем с того, что в случае некоторых трюков с конфигом у меня и классов то не густо . Как по фен шую, я прекрасно сам знаю, и чаще всего рекомендациям следую, любое отклонение от рекомендаций я могу обосновать и даже во всяких ReadMe пишу почему так сделал. Естественно функции в большинстве случаев у меня чистые, никаких неявных зависимостей нет. Точнее почти нет. У тебя неявные зависимости тоже есть, например Locale. Запускаешь в одном окружении, у тебя так даты выводятся и запятая отображается точкой, а в другом окружении наоборот.
Ну и мой изначальный пост был немного ошибочным, я просто неверно воспринял выражение ConfigHelper().getFooValue(). Воспринял как new ConfigHelper().getFooValue(), слишком привыкнув к Java конвенции именовать функции и методы начиная со строчной буквы. А любовь ко всяким new для выполнения простейших действий даже в стандартной библиотеке меня несколько огорчает, а многие подобные подходы и в своем коде используют, что меня сильно печалит.
·>"Ceylon Stable release 1.3.3 / August 21, 2017; 4 years ago", ну да, ничего страшного.
Я как бы в курсе . На деле весьма хороший был язык, вроде как полностью перекрывается по фичам scala 3, которую все никак не соберусь попробовать. Некоторых фичей сейчас реально не хватает.
Здравствуйте, AlexGin, Вы писали:
AG>>Для представления древовидных структур, с коллекциями элементов того же типа, что и корневой элемент? AG>>Когда в корневом элементе требуется иметь коллекцию child-элементов.
BFE>>Не вижу препятствий.
AG>Элемент дерева, как логично я заметил выше, один и тот же. AG>Для каких-то других элементов — он является parent-ом. AG>Для других — чайлдом. AG>Ссылочку на этот чайлд хранит родитель (parent) в своей коллекции.
Тогда смотрите ответ Точки.
AG>То, что Вы писали — это два разных класса. AG>Я бы предложил так:
AG>
AG>Здесь и у Вас, и у меня — две сушности. AG>Но у меня — универсальный узел и интерфейс к нему. AG>Именно наличие программного интерфейса — позволяет уменьшить связанность (coupling) в проекте. AG>За счёт этого, получим упрощение архитектуры и уменьшение количества ошибок.
Для того, чтобы уменьшить связность, виртуальные методы не нужны:
AG>Конечно же, можно было обойтись и без интерфейса, однако так красивее и понятнее.
Нет. Если вы хотите, чтобы элемент дерева был один и тот же, то он и должен быть один и тот же. Хотите уменьшить связность, то можно сделать, как я показал. Просто и понятно. А интерфейсы нужны там, где есть коллекции разных типов обрабатываемых в едином конвейере. Скажем если листья дерева, корень дерева и промежуточные узлы хранят разную информацию, то вот тогда следует применить интерфейс в виде класса с абстрактными методами.
AG>Кроме того, имеется заложенная база — как для развития (новые типы узлов),
Вот! Я видел очень много проектов, где была заложена база для развития и это развитие никогда не наступало. Более того, через несколько лет оказывалось, что развивать надо совсем не то, что предполагалось изначально, а там, где оно предполагалось развития никакого не надо. В результате вместо упрощения имеем усложнение из-за дополнительных классов, которые никому не нужны.
AG>так и для тестирования основы нашего проекта.
Про связь с тестированием я не понял.
Здравствуйте, elmal, Вы писали:
BFE>>В конторе, где я сейчас работаю, прямо в правилах кодирования есть пункт запрещающий создавать интерфейс если его реализация будет ровно в одном классе. Причин не знаю. E>Причина проста. Кода должно быть ровно столько, сколько необходимо. Минимальное количество кода — имеем одну реализацию. Потребовалась другая — выделяем интерфейс. Выделяем когда потребуется, а не потому, что так "положено". E>Меньше кода — это легче читается, легче читается — проще понимание, проще понимание — делаешь меньше ошибок, делаешь меньше ошибок — задача быстрее будет сдана. Меньше кода — это более легкие и более быстрые изменения, это меньше проблемами с мержем с другими и т.д. При развитии можно все сложности во всякие библиотеки и утилиты добавлять, а основной код должен быть минимальным по объему. А библиотеки и утилиты — там вообще никаких классов, там тупо набор легко тестируемых функций.
Какой-то странный подход.
Во-первых: легче читается и понимается тот код, который написан в привычном читателю стиле с использованием известных читателю приёмов. (Это, конечно, при отсутствии не выраженных в коде зависимостей).
Во-вторых: минимальное количества кода — это что? Например, если завести глобальную переменную для индекса всех циклов программы, то это ведь уменьшит код. Пример, конечно, абсурдный, но я такое видел.
Прекрасно!
У нас уже (с Вашей подачи) — две равнозначные сущности.
Там, напомним, где в принципе достаточно одной (плюс вспомогательная: интерфейс).
Я вот посмотрел и подумал — есть Node, есть SubNode.
Есть Forward Declaration, которое так и не решило проблему перекрёстных ссылок.
Зачем так сложно?
Почему так не-KISS-ло всё накручено?
Неудивительно, что это всё даже не компилируется
AG>>так и для тестирования основы нашего проекта. BFE>Про связь с тестированием я не понял.
Здравствуйте, elmal, Вы писали:
E>У тебя неявные зависимости тоже есть, например Locale. Запускаешь в одном окружении, у тебя так даты выводятся и запятая отображается точкой, а в другом окружении наоборот.
Обычно я избегаю такие моменты типа системной локали или таймзоны. Все функции позволяют передавать локаль явно. Если зависимость от системных настроек, то это может ломать, например, тесты, когда они выполняются у разных девов на их компе.
Системная локаль/tz инжектится в wiring root где-нибдуь в main уже самого приложения.
E>Ну и мой изначальный пост был немного ошибочным, я просто неверно воспринял выражение ConfigHelper().getFooValue(). Воспринял как new ConfigHelper().getFooValue(), слишком привыкнув к Java конвенции именовать функции и методы начиная со строчной буквы. А любовь ко всяким new для выполнения простейших действий даже в стандартной библиотеке меня несколько огорчает, а многие подобные подходы и в своем коде используют, что меня сильно печалит.
Не очень ясно что именно огорчает, можно пример? В случае какого-нибудь там new ArrayList я не вижу никаких проблем.
С new ConfigHelper() неясно откуда конфиг собственно браться будет, не из глобальной же переменной опять, поэтому инжектим зависимость.
E>·>"Ceylon Stable release 1.3.3 / August 21, 2017; 4 years ago", ну да, ничего страшного. E>Я как бы в курсе . На деле весьма хороший был язык, вроде как полностью перекрывается по фичам scala 3, которую все никак не соберусь попробовать. Некоторых фичей сейчас реально не хватает.
Да не знаю, современная java делает ненужной все эти языки. По крайней мере, в подавляющем большинстве случаев.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Не очень ясно что именно огорчает, можно пример? В случае какого-нибудь там new ArrayList я не вижу никаких проблем. ·>С new ConfigHelper() неясно откуда конфиг собственно браться будет, не из глобальной же переменной опять, поэтому инжектим зависимость.
Меня в свое время дико бомбил класс Calendar, когда чтоб сконструировать дату приходилось делать инстанс календаря и потом там делать кучу всякого, каждый раз в новом проекте приходилось лисапеды писать чтоб нормально работать с датой. Сейчас конечно с LocalDate все поприятнее.
·>Да не знаю, современная java делает ненужной все эти языки. По крайней мере, в подавляющем большинстве случаев.
Дело привычки. В современной Java по прежнему нет Nullability, и это физически не исправить из за необходимости держать обратную совместимость. Привык если к тому, что String и String? это существенная разница — уже в чистой Java дискомфорт будет. top level функций по прежнему нет, как и extension функций — если привык, отсутствие вызывает дискомфорт. Ну и система типов — Union Types, Intersection Types — когда ими проникся, их отсутствие вызывает дискомфорт, реально классная штука, которая сейчас в Scala 3 насколько я понимаю только есть. И куча всего еще вроде необходимости операции new, точки с запятой и т.д — когда привыкаешь к хорошему, уже стандартный синтаксис выглядит громоздко. Java конечно лучше становится, но медленно.
Здравствуйте, elmal, Вы писали:
E>·>Не очень ясно что именно огорчает, можно пример? В случае какого-нибудь там new ArrayList я не вижу никаких проблем. E>·>С new ConfigHelper() неясно откуда конфиг собственно браться будет, не из глобальной же переменной опять, поэтому инжектим зависимость. E>Меня в свое время дико бомбил класс Calendar, когда чтоб сконструировать дату приходилось делать инстанс календаря и потом там делать кучу всякого, каждый раз в новом проекте приходилось лисапеды писать чтоб нормально работать с датой. Сейчас конечно с LocalDate все поприятнее.
Эх, это да. Это очень старинный класс и проектировался в своё время в своих условиях.
Ещё меня checked exceptions достают, особенно с лямбдами.
E>·>Да не знаю, современная java делает ненужной все эти языки. По крайней мере, в подавляющем большинстве случаев. E>Дело привычки. В современной Java по прежнему нет Nullability, и это физически не исправить из за необходимости держать обратную совместимость. Привык если к тому, что String и String? это существенная разница — уже в чистой Java дискомфорт будет.
Согласен. Впрочем, если очень хочется, есть https://checkerframework.org/manual/#nullness-checker
E>top level функций по прежнему нет, как и extension функций — если привык, отсутствие вызывает дискомфорт.
Хм... в смысле реверсить композицию? Вместо f(g(h(x))) писать x.h().g().f()? Ну... не знаю, да, может дело привычки.
А в скале операторы достают. Иероглифы какие-то. Написан какой-нибудь <: и догадайся что за х и как такое гуглить.
E>Ну и система типов — Union Types, Intersection Types — когда ими проникся, их отсутствие вызывает дискомфорт, реально классная штука, которая сейчас в Scala 3 насколько я понимаю только есть.
Ну да... прикольно. В typescript такое есть, удобно.
Кстати, появились sealed classes, но честно говоря пока не придумал когда их надо использовать на практике.
E>И куча всего еще вроде необходимости операции new, точки с запятой и т.д — когда привыкаешь к хорошему, уже стандартный синтаксис выглядит громоздко. Java конечно лучше становится, но медленно.
Всё равно большинство кода Идея за тебя пишет.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, B0FEE664, Вы писали:
C>>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>>В этом есть какой-то тайный смысл, или они просто идиоты?
BFE>В конторе, где я сейчас работаю, прямо в правилах кодирования есть пункт запрещающий создавать интерфейс если его реализация будет ровно в одном классе. Причин не знаю.
А можно прямую цитату? Выделенное можно трактовать по-разному.
P.S. Объясню, что имеется в виду. Есть ситуации, когда программист предполагает, что реализация будет только одна. И есть ситуации, когда программист хочет подчеркнуть, что реализаций возможно много, но нам сейчас нужна только одна. Интересно узнать, какой из этих случаев подразумевается. Во втором случае часто говорят «ЯГНИ!», что лично я считаю неверным. (По крайней мере, когда я вижу, что в результате получается).
Здравствуйте, elmal, Вы писали:
N>>Кто сказал? Конфиг это контекст. Контекстов может быть несколько. Например, несколько экземпляров стека протоколов на разных IP адресах или портах, с разными свойствами. E>Я вообще то считал, что конфиг — это такое условно неизменное что то, а контекст — это что то постоянно меняющееся, соответственно это все совершенно разные вещи.
Давай я тебе проиллюстрирую.
Предположим, написал ты некий компонент, который умеет что то доставать и что то класть в БД. Коннект к БД прописываешь в статическом конфиге.
А теперь прибегает к тебе продакт и просит сделать утилиту для переноса данных из одной БД в другую. С использованием твоего компонента. Осознал проблему?
Здравствуйте, elmal, Вы писали:
E>Не всегда нужны классы, иногда для простоты и удобства гораздо проще до черта логики сделать обычными top level функциями.
Ага. Называется "херак-херак и в продакшн".
E> Во вторых, если классы все таки нужны, без DI фреймворков ты задолбаешься этот конфиг в каждый класс прокидывать и инициализировать.
Зависит от количества классов и их структуры. Никто не мешает использовать некий RootContext, тогда протаптывать нужно будет только в один класс.
Здравствуйте, baxton_ulf, Вы писали:
_>да я что спорю что-ли? все правильно, все рефакторится. но если бы тесты писались вместе с кодом то рефакторить не пришлось бы.
Лучше не придумывать будущие требования, а писать код под требования здесь и сейчас, и при этом максимально удобный для рефакторинга. Проверено десятками, если не сотнями тысяч проектов.
Здравствуйте, B0FEE664, Вы писали:
BFE>В конторе, где я сейчас работаю, прямо в правилах кодирования есть пункт запрещающий создавать интерфейс если его реализация будет ровно в одном классе. Причин не знаю.
Скорее всего причина в каком то заваленном из-за возомнивших себя крутыми архитектами разработчиков. Мне один такой проект как то пришлось поддерживать некоторое время.
Да и сейчас мне на ревью молодежь иногда пишет — а почему ты сервис не через интерфейс в DI протащил? У многих свеженанятых прям императив — если есть сервис в DI, у него обязательно должен быть интерфейс, даже если сервис internal и никаких моков к нему не планируется.
Здравствуйте, elmal, Вы писали:
НС>>Осознал проблему? E>Так нет никакой проблемы. Если пишется именно утилита для повторного использования, то будешь изначально предполагать, что пользователь захочет много чего интересного и статический конфиг не подойдет.
Или нет.
E>Если изначально писалась простенькая утилитка для скриптинга на нормальном языке, то по умолчанию будет статический конфиг, возможно даже хардкодом для скорости. Если же вдруг задача оказывается типичной и захочется именно повторного использования — это все очень быстро рефакторится
Вот проблема как раз в том, что глобальные переменные это очень больно для рефакторинга, ибо потенциально может неявно затронуть непредсказуемое количество кода.
Здравствуйте, Ночной Смотрящий, Вы писали:
E>>У тебя неявные зависимости тоже есть, например Locale. НС>Не знаю как в джаве, а в дотнете локаль это не static, а thread static. Это уже несколько иной коленкор, хотя тоже не фонтан.
В java есть дефолтная локаль виртуальной машины (т.е. глобальная переменная, проставляется из операционки, но можно поменять) и плюс все локаль-зависимые методы могут принимать локаль явно. Т.е. например string.toLowerCase() == string.toLowerCase(Locale.getDefault())
Thread static — это вредительство какое-то в нашу эпоху асинков...
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, netch80, Вы писали:
N>·>Thread static — это вредительство какое-то в нашу эпоху асинков... N>А переключатель контекста асинков разве ещё не способен потянуть за собой нужные параметры?
Может и протягивает там где протягивается... Но думаю это легко может поломаться, если использовать какую-нибудь необычную передачу данных между тредами. В любом случае, по-моему это будет очень весело разбираться если что-то где-то не так протащится. Вместо одной глобальной переменной, у нас теперь неожиданно целая их коллекция.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>В java есть дефолтная локаль виртуальной машины (т.е. глобальная переменная, проставляется из операционки, но можно поменять) и плюс все локаль-зависимые методы могут принимать локаль явно. Т.е. например string.toLowerCase() == string.toLowerCase(Locale.getDefault())
Печаль.
·>Thread static — это вредительство какое-то в нашу эпоху асинков...
В нашу эпоху асинков thread static превратился в AsyncLocal и соотв. служебный код, обеспечивающий корректную инициализацию локалей.
Здравствуйте, ·, Вы писали:
·>Может и протягивает там где протягивается... Но думаю это легко может поломаться, если использовать какую-нибудь необычную передачу данных между тредами. В любом случае, по-моему это будет очень весело разбираться если что-то где-то не так протащится. Вместо одной глобальной переменной, у нас теперь неожиданно целая их коллекция.
Практика показывает, что твои думы это что то вроде ветряных мельниц.
Вроде что-то улучшили в 4.6. Но всё равно явно корявая архитектура. _Переменные_ не стоит делать глобальными. Thread local нужен совсем для другого. Ну может кеш какой-нибудь или типа того, но нельзя там хранить изменяемое состояние.
А ещё этот идиотизм с двумя переменными Thread.CurrentUICulture и Thread.CurrentCulture. И всё это понапихали в thread.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Codealot, Вы писали:
C>Некоторые люди делают для каждого класса по интерфейсу и фабрике чтобы создавать объекты, причем каждый интерфейс реализован ровно в одном классе. C>В этом есть какой-то тайный смысл, или они просто идиоты?
Просто идиоты конечно. Если спросить, скажут: ну чтоб гибкость, низкая связанность, на будущее
Здравствуйте, rosencrantz, Вы писали:
C>>В этом есть какой-то тайный смысл, или они просто идиоты?
R>Просто идиоты конечно. Если спросить, скажут: ну чтоб гибкость, низкая связанность, на будущее
Не идиоты, а просто осторожные люди. Не у всех же есть смелость сказать что король голый. Некоторым надо кормить пятерых детей
Вот как научили их так молиться, они так и будут. Интерфейсы, фабрики, все дела.
Здравствуйте, AlexGin, Вы писали: AG>Прекрасно! AG>У нас уже (с Вашей подачи) — две равнозначные сущности. AG>Там, напомним, где в принципе достаточно одной (плюс вспомогательная: интерфейс).
Хотите одну? Вот вам одна сущность:
class node
{
std::vector<std::shared_ptr<node>> childs;
};
AG>Я вот посмотрел и подумал — есть Node, есть SubNode. AG>Есть Forward Declaration, которое так и не решило проблему перекрёстных ссылок.
Что такое "проблема перекрёстных ссылок"? AG>Зачем так сложно?
Не знаю. Чем простой вариант устраивает? AG>Почему так не-KISS-ло всё накручено?
Ну вы же сами хотели ограничить связность. AG>Неудивительно, что это всё даже не компилируется
Вы это пытались компилировать? Зачем?
Кто и где предлагал замедлять продакшн код? Что есть фактор замедления?
P.S. Если ты предполагаешь, что вызов через таблицу виртуальных функций (vtable) вносит замедление,
то для аппаратуры последних 15 лет это уже не актуально.
Если приходится изменять код, чтобы подстроится под тесты, значит с тестами что-то не так.
AG>P.S. Если ты предполагаешь, что вызов через таблицу виртуальных функций (vtable) вносит замедление, AG>то для аппаратуры последних 15 лет это уже не актуально.
Это память не актуальна, а вот скорость... Всё так же тормозят приложения и системы.