Re: Класс ради класса
От: Shtole  
Дата: 14.08.22 19:52
Оценка:
Здравствуйте, elmal, Вы писали:

E>Так вот, я молчу про то, что в бизнес логике вызываются напрямую всякие методы UI отрисовки вида увеличить прогресс бар.


ИМХО, гораздо хуже сабжа. Сабж может быть оправдан архитЕктурно (даже при performance penalty), а вот это оправдать ничем нельзя, это просто дисквалификация.
Do you want to develop an app?
Отредактировано 15.08.2022 11:21 Shtole . Предыдущая версия .
Re[4]: Класс ради класса
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 14.08.22 20:11
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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


Сколько лет в табуреточной индустрии?
Маньяк Робокряк колесит по городу
Re[5]: Класс ради класса
От: Pavel Dvorkin Россия  
Дата: 15.08.22 02:50
Оценка:
Здравствуйте, Marty, Вы писали:

M>Сколько лет в табуреточной индустрии?


С тех времен, когда табуретки еще делали из натурального дерева
With best regards
Pavel Dvorkin
Re[2]: Класс ради класса
От: vaa  
Дата: 15.08.22 03:22
Оценка:
Здравствуйте, Shtole, Вы писали:

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


E>>Так вот, я молчу про то, что в бизнес логике вызываются напрямую всякие методы UI отрисовки вида увеличить прогресс бар.


S>ИМХО, гораздо хуже сабжа. Сабж может быть оправдан архитиктурно (даже при performance penalty), а вот это оправдать ничем нельзя, это просто дисквалификация.

возможно берут на поддержку такого же овна, тогда это тоже оправдано, правильный чел не поймет. (как в джокере: — что смешного? — вы все равно не поймете.)
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[3]: Класс ради класса
От: Буравчик Россия  
Дата: 15.08.22 03:24
Оценка:
Здравствуйте, elmal, Вы писали:

E>Здравствуйте, Буравчик, Вы писали:


Вот ещё доводы в пользу обычного класса (без статиков)
https://stackoverflow.com/questions/205689/class-with-single-method-best-approach


  Цитата

I used to love utility classes filled up with static methods. They made a great consolidation of helper methods that would otherwise lie around causing redundancy and maintenance hell. They're very easy to use, no instantiation, no disposal, just fire'n'forget. I guess this was my first unwitting attempt at creating a service oriented architecture — lots of stateless services that just did their job and nothing else. As a system grows however, dragons be coming.

Polymorphism
Say we have the method UtilityClass.SomeMethod that happily buzzes along. Suddenly we need to change the functionality slightly. Most of the functionality is the same, but we have to change a couple of parts nonetheless. Had it not been a static method, we could make a derivate class and change the method contents as needed. As it's a static method, we can't. Sure, if we just need to add functionality either before or after the old method, we can create a new class and call the old one inside of it — but that's just gross.

Interface woes
Static methods cannot be defined through interfaces for logic reasons. And since we can't override static methods, static classes are useless when we need to pass them around by their interface. This renders us unable to use static classes as part of a strategy pattern. We might patch some issues up by passing delegates instead of interfaces.

Testing
This basically goes hand in hand with the interface woes mentioned above. As our ability of interchanging implementations is very limited, we'll also have trouble replacing production code with test code. Again, we can wrap them up but it'll require us to change large parts of our code just to be able to accept wrappers instead of the actual objects.

Fosters blobs
As static methods are usually used as utility methods and utility methods usually will have different purposes, we'll quickly end up with a large class filled up with non-coherent functionality — ideally, each class should have a single purpose within the system. I'd much rather have a five times the classes as long as their purposes are well defined.

Parameter creep
To begin with, that little cute and innocent static method might take a single parameter. As functionality grows, a couple of new parameters are added. Soon further parameters are added that are optional, so we create overloads of the method (or just add default values, in languages that support them). Before long, we have a method that takes 10 parameters. Only the first three are really required, parameters 4-7 are optional. But if parameter 6 is specified, 7-9 are required to be filled in as well... Had we created a class with the single purpose of doing what this static method did, we could solve this by taking in the required parameters in the constructor, and allowing the user to set optional values through properties, or methods to set multiple interdependent values at the same time. Also, if a method has grown to this amount of complexity, it most likely needs to be in its own class anyways.

Demanding consumers to create an instance of classes for no reason
One of the most common arguments is, why demand that consumers of our class create an instance for invoking this single method, while having no use for the instance afterwards? Creating an instance of a class is a very very cheap operation in most languages, so speed is not an issue. Adding an extra line of code to the consumer is a low cost for laying the foundation of a much more maintainable solution in the future. And finally, if you want to avoid creating instances, simply create a singleton wrapper of your class that allows for easy reuse — although this does make the requirement that your class is stateless. If it's not stateless, you can still create static wrapper methods that handle everything, while still giving you all the benefits in the long run. Finally, you could also make a class that hides the instantiation as if it was a singleton: MyWrapper.Instance is a property that just returns new MyClass();

Only a Sith deals in absolutes
Of course, there are exceptions to my dislike of static methods. True utility classes that do not pose any risk to bloat are excellent cases for static methods — System.Convert as an example. If your project is a one-off with no requirements for future maintenance, the overall architecture really isn't very important — static or non static, doesn't really matter — development speed does, however.

Standards, standards, standards!
Using instance methods does not inhibit you from also using static methods, and vice versa. As long as there's reasoning behind the differentiation and it's standardised. There's nothing worse than looking over a business layer sprawling with different implementation methods.

Best regards, Буравчик
Re[4]: Класс ради класса
От: elmal  
Дата: 15.08.22 05:16
Оценка:
Здравствуйте, Буравчик, Вы писали:

Во первых, даже здесь:

Only a Sith deals in absolutes
Of course, there are exceptions to my dislike of static methods. True utility classes that do not pose any risk to bloat are excellent cases for static methods — System.Convert as an example. If your project is a one-off with no requirements for future maintenance, the overall architecture really isn't very important — static or non static, doesn't really matter — development speed does, however.

Тестовое задание, это как раз такой случай — система никогда не будет расти,более того, там как раз утилитный класс вроде System.Convert.

Во вторых, как в этом топике другие сказали — есть принципы KISS и YAGNI.

И в третьих. Когда "As a system grows however, dragons be coming." Когда система растет, скорее всего будут использоваться DI IOC фреймворки и уже тема для холивара не становится актуальной, мы сами никакой оператор new не используем, за нас это делает фреймворк и он же занимается начальной инициализацией. Более того, на начальном этапе скорее всего будут использоваться фреймворки.
Re[2]: Класс ради класса
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 15.08.22 07:28
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Идеологически — создание нового экземпляра класса верно и оправданно.

С чего бы это? ООП это же про внутреннее состояние. Если его не надо хранить, то смысла в объектах нет — достаточно просто написать функцию и всё. Для обрработки или вычиисления чего либо достаточно функции, а если её применить совместно с стрим апи каким-нибудь, то будет вообще всё красиво.

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

Только тебе не табуретка нужна, а сеть где-то.

PD>Увы, это стало почти что нормой и объяснить , что это плохо, очень трудно.

Да нифига. Там где надо требуют понимание работы GC, синхронизации и disposal всяких.
Sic luceat lux!
Re[3]: Класс ради класса
От: Pavel Dvorkin Россия  
Дата: 15.08.22 08:48
Оценка: +1
Здравствуйте, Kernan, Вы писали:

K>С чего бы это? ООП это же про внутреннее состояние. Если его не надо хранить, то смысла в объектах нет — достаточно просто написать функцию и всё. Для обрработки или вычиисления чего либо достаточно функции, а если её применить совместно с стрим апи каким-нибудь, то будет вообще всё красиво.


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

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


PD>>Увы, это стало почти что нормой и объяснить , что это плохо, очень трудно.

K>Да нифига. Там где надо требуют понимание работы GC, синхронизации и disposal всяких.

Ключевое слово "Где надо". Ни из чего не следует, что тот, о ком пишет ТС, работал "там где надо"
With best regards
Pavel Dvorkin
Re[4]: Класс ради класса
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 15.08.22 09:43
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Потому что с некоторых пор в наши головы начали вбивать мысль (и довольно успешно, надо сказать), что просто написать код по имеющемуся ТЗ — этого недостаточно. Надо еще позаботиться о том, чтобы это могло рефакториться, чтобы тот, кто будет это изменять, мог легко и просто превратить эти брюки в элегантные шорты и т.д.

Что-то мне кажется что ты давно не работал над масштабнымии вещами.

PD>И тут автор начинает задумываться. А что , если со временем этот процесс калькуляции будет изменен ? Если в нем будет не простенькая формула, а несколько стадий, с передачей данных между ними ?

И что? Несколько стадий неплохо разбиваются на несколько функций.

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

Чего? Я говорил про статическую ФУНКЦИЮ в классе которая только считает т.к. в шарпах нет просто функций, класс без полей если что. Такое многопоточится на раз если функция не даёт "сайд эффектов" или как там это называется. А ты добавил полей, а кто-то заюзал один экземпляр твоего калькулятора в нескольких потоках и у него всё заработало, а вот в проде всё не заработало потому что дата рейсы никуда не пропали.
Sic luceat lux!
Re[3]: Класс ради класса
От: scf  
Дата: 15.08.22 10:00
Оценка: +1
Здравствуйте, Kernan, Вы писали:

K>С чего бы это? ООП это же про внутреннее состояние. Если его не надо хранить, то смысла в объектах нет — достаточно просто написать функцию и всё. Для обрработки или вычиисления чего либо достаточно функции, а если её применить совместно с стрим апи каким-нибудь, то будет вообще всё красиво.


В современном коде очень немного классов с мутабельным состоянием. 99% классов пишутся для отделения интерфейса от реализации и Dependency Injection. Прежде всего для того, чтобы иметь возможность добавлять зависимости в функцию и менять реализацию, не затрагивая вызывающий код.
Re[5]: Класс ради класса
От: Pavel Dvorkin Россия  
Дата: 15.08.22 10:23
Оценка:
Здравствуйте, Kernan, Вы писали:

PD>>Потому что с некоторых пор в наши головы начали вбивать мысль (и довольно успешно, надо сказать), что просто написать код по имеющемуся ТЗ — этого недостаточно. Надо еще позаботиться о том, чтобы это могло рефакториться, чтобы тот, кто будет это изменять, мог легко и просто превратить эти брюки в элегантные шорты и т.д.

K>Что-то мне кажется что ты давно не работал над масштабнымии вещами.

Верно кажется. Но то, о чемя написал, существует уже давно. Вот когда я начинал, этим точно не заморачивались.

PD>>И тут автор начинает задумываться. А что , если со временем этот процесс калькуляции будет изменен ? Если в нем будет не простенькая формула, а несколько стадий, с передачей данных между ними ?

K>И что? Несколько стадий неплохо разбиваются на несколько функций.

См. ниже.

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

K>Чего? Я говорил про статическую ФУНКЦИЮ в классе которая только считает т.к. в шарпах нет просто функций, класс без полей если что. Такое многопоточится на раз если функция не даёт "сайд эффектов" или как там это называется.

Я же написал — а если потом появятся поля для передачи промежуточных данных ? Я же написал.


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


Откуда тут дата рейсы ? В каждом потоке свой экземпляр этого калькулятора, общих данных нет. А вот со static не получится.
With best regards
Pavel Dvorkin
Re[3]: Класс ради класса
От: Shtole  
Дата: 15.08.22 11:34
Оценка: 5 (1)
Здравствуйте, vaa, Вы писали:

E>>>Так вот, я молчу про то, что в бизнес логике вызываются напрямую всякие методы UI отрисовки вида увеличить прогресс бар.


S>>ИМХО, гораздо хуже сабжа. Сабж может быть оправдан архитиктурно (даже при performance penalty), а вот это оправдать ничем нельзя, это просто дисквалификация.


«архитиктурно» — ужас какой. Сорри.

vaa>возможно берут на поддержку такого же овна, тогда это тоже оправдано, правильный чел не поймет. (как в джокере: — что смешного? — вы все равно не поймете.)


Меня как-то брали. В плохом смысле этого слова (такое вот ощущение духовного изнасилования). Я не хотел, но не устоял перед уровнем оплаты. Честно скажу, понимать там особо нечего (ну, или я неправильный). Просто выносишь очередную тысячу строк, где интерпретируется скрипт, управляющий оборудованием, из OnCombo1Change() в отдельный класс и скрещиваешь пальцы, чтобы у тысяч клиентов после этого ничего не отпало. (Тестов-то тоже нет, да их и не написать толком). Ну и, конечно, прямые отрисовки заменяешь на колбэки/сообщения.
Do you want to develop an app?
Re: Класс ради класса
От: sergii.p  
Дата: 15.08.22 13:17
Оценка: 6 (1) +1
Здравствуйте, elmal, Вы писали:

E>Короче, важно быстродействие, кое что выполняется в цикле. По смыслу — выполнение функции, один вход, один выход. Однако автор создает экземпляр определенного класса через new без параметров и далее выполняет функцию calculate, тоже без параметров. После чего созданный объект забывается и в цикле создается новый.


мне кажется, тут ноги растут из просчёта языка, что нельзя создать свободную функцию. Вот и приходится добавлять "классы ради классов" с функциональностью областей видимости (namespace). А то как оно вызывается — абсолютно незначимый момент.
Я даже не понимаю зачем это ограничение до сих пор в стандарте. Можно было уже сотню раз раскаяться и не плодить костыли вроде static class.

В общем, какой язык, такие и программисты
Re[4]: Класс ради класса
От: Sharov Россия  
Дата: 15.08.22 17:32
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Все зависит от того, чем именно он занимался.

PD>Где-то такое приведет к приличной просадке скорости или проблемам с памятью. А где-то ничего заметного не будет. Например, в GUI — пока приложение вызовет ОС для отрисовки, а ОС перебросит все это видеодрайверу, а тот будет думать, как это лучше отрисовать, потому что у него таких приложений много, а потом начнет шрифты рендерить.. На этом фоне лишний new — что слону дробина.

Я бы так не сказал, учитывая что после видеодрайвера работу начнет соотв. железо, т.е. это будет быстро и по памяти не очень много сожрет ( хотя зависит от).
А вот множество new и по времени и по памяти да еще + GC могут дорого обойтись.
Кодом людям нужно помогать!
Re[2]: Класс ради класса
От: vaa  
Дата: 15.08.22 23:27
Оценка:
Здравствуйте, sergii.p, Вы писали:

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


E>>Короче, важно быстродействие, кое что выполняется в цикле. По смыслу — выполнение функции, один вход, один выход. Однако автор создает экземпляр определенного класса через new без параметров и далее выполняет функцию calculate, тоже без параметров. После чего созданный объект забывается и в цикле создается новый.


SP>мне кажется, тут ноги растут из просчёта языка, что нельзя создать свободную функцию. Вот и приходится добавлять "классы ради классов" с функциональностью областей видимости (namespace). А то как оно вызывается — абсолютно незначимый момент.

SP>Я даже не понимаю зачем это ограничение до сих пор в стандарте. Можно было уже сотню раз раскаяться и не плодить костыли вроде static class.

SP>В общем, какой язык, такие и программисты


Полностью согласен стиль программирования в ЯП зависит от культуры сообщества.
Локальные функции добавили, но ввиду мутабельности и null по умолчанию в шарпе они не очень удобны,
да еще могут порождать трудноуловимые мутации внешних к фукции переменных.
вообщем фигурные скобки это диагноз. но яп не худший, если учесть отличную ВМ и базовые библиотеки типа линка.
больше всего удручает его многословность при создании массивов и словарей например и определение типа перед именем. было бы лучше
name : string;

имя это строка.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[4]: Класс ради класса
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 16.08.22 05:17
Оценка:
Здравствуйте, scf, Вы писали:

scf>В современном коде очень немного классов с мутабельным состоянием.

Наверное, в большинстве современных проектов где основные задачи это преобразование данных из сетевой трубы в трубу БД, а не управление состояниями. Я, видимо, в своём пузыре с управлением состояниями сидел последние лет 10.
scf>99% классов пишутся для отделения интерфейса от реализации и Dependency Injection. Прежде всего для того, чтобы иметь возможность добавлять зависимости в функцию и менять реализацию, не затрагивая вызывающий код.
Но ведь этого можно добиться функциями высшего порядка. Ладно, в любом случае это всё сорта одного и того же.
Sic luceat lux!
Re[2]: Класс ради класса
От: Буравчик Россия  
Дата: 16.08.22 07:24
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>мне кажется, тут ноги растут из просчёта языка, что нельзя создать свободную функцию. Вот и приходится добавлять "классы ради классов" с функциональностью областей видимости (namespace). А то как оно вызывается — абсолютно незначимый момент.


Большой разницы между статическими методами и настоящими функциями нет.
Модуль с функциями = класс со статическими методами.

Вышеперечисленные проблемы присутствуют у обоих (в отличии от обычного класса).
Best regards, Буравчик
Re[3]: Класс ради класса
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.08.22 11:07
Оценка:
Здравствуйте, elmal, Вы писали:

E>
E>var calculator = new Calculator();
E>var result = calculator.calculate(data);
E>


E>И ведь не лень ...

Надо смотреть весь код. Возможно, Calulator — это struct, и там никакого "выделения памяти" не происходит, а происходит обнуление состояния этого калькулятора.
Возможно, там вызов calculate проинлайнился, и вообще вся инициализация выбросилась JIT-ом вместе с экземпляром, и даже регистр этой переменной не занят.
Возможно, этот код написан внутри замыкания, и calculator был залифтен в поле класса, которое устраняется джитом только в том случае, если инициализация проведена прямо здесь, чтобы инлайнер её видел.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Класс ради класса
От: _FRED_ Черногория
Дата: 17.08.22 10:14
Оценка: +2
Здравствуйте, elmal, Вы писали:

E>…Однако автор создает экземпляр определенного класса через new без параметров и далее выполняет функцию calculate, тоже без параметров. После чего созданный объект забывается и в цикле создается новый.

E>Я был только сторонний наблюдатель и на это посмотрел после того, как это было отправлено, просто как сторонний консультант по просьбе автора. Непосредственно тех, кто оценивал тестовое задание — все удовлетворило. Отсюда вопрос, я что — действительно слишком придираюсь, или по крайней мере в C# является бест практикой на каждый чих создавать класс с одним публичным методом и чтоб вызывающий код для простейшего случая не вызывал одну функцию, а вынужден сначала создавать объект оператором new, а затем вызывать один единственный метод чтоб что то посчитать?

Вопрос было бы правильнее всего адресовать автору кода: зачем он сделал так, а не как-то иначе. Возможно, он предвидел, что а) в реальной практике нужно будет поменять так и эдак б) при необходимости добавить тесты будет проще расширитьть этот класс в) да всё что угодно. ИМХО, оценивание тестового задания без беседы потом о том, как оно выполненно и почему скорее отвечаетнг а вопрос "очень ли оно плохо сделано", а чтобы разобраться с тем, на сколько оно сделано хорошо имеет смысл поговоритоь с автором и тогда вещи, которые вызывают недоумение при первом ревью могут проясниться.
Help will always be given at Hogwarts to those who ask for it.
Re: Класс ради класса
От: maxkar  
Дата: 17.08.22 11:39
Оценка: 145 (4) +1
Здравствуйте, elmal, Вы писали:

E>Так вот, я молчу про то, что в бизнес логике вызываются напрямую всякие методы UI отрисовки вида увеличить прогресс бар.

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

E>Все отправлено в виде одного коммита в GIT, и не видно истории как все делалось — допустим тоже так и надо.

Тоже связано с тем, что с абстракциями не очень (правда уже применительно к процессу разработки). Не факт, что там вообще было промежуточное работающее остояние, которое можно было бы закоммитить. А может быть он из среды, где все коммиты сливаются в один при мерже, так зачем стараться?

E>Но мне вот интересно, это я такой непродвинутый и это только меня напрягает?

Меня такое тоже напрягает. Потому что смысла там мало (кстати, комментариев/документации к классу и методу тоже отсутствуют?). Смотрите, как это делается.

Допустим, мы решили, что какую-то функциональность нужно вынести из места использования (т.е. вызывающего класса) куда-то извне. Потому что она сложная и/или полезная. Фунцкиональность у вас без побочных эффектов. Поэтому API у нее будет статическим, это обеспечивает меньшее зацепление (coupling) между клиентами и реализацией. Например,

public class Calculator {
  private Calculator() {
    throw new UnsupportedOperationException("Don't even try");
  }

  public static Result calculate(Input input) {
    ...
  }
}


Там делаем вычисления. В ряде случаев бывает удобно иметь внутреннее состояние на время вычисления. Всякие парсеры могут входные данные и позицию разбора разделять между многими методам и т.п. Но это детали реализации. Поэтому мы можем сохранить API и изменить реализацию:

public class Calculator {
  private final Input input;
  private Calculator(Input input) {
    this.input = input;
  }

  private Result calculateImpl() {
    ...
  }

  public static Result calculate(Input input) {
    final Calculator calc = new Calculator(input);
    ...
    return calc.calculateImpl();
  }
}


Это может быть один метод к calculate, может быть — много вызовов. Со временем код может рефакториться дальше. Например, в процессе работы образовалось несколько удобных утилитарных классов и вычисления можно сделать через них. В этом случае статический метод будет изменен на создание/вызов этих новых утилит вместо калькулятора. Прежняя реализация будет не нужна и может быть выкинута. С классом это все будет выглядеть гораздо менее естественно.

Еще плюсы. В статических методах можно делать всякую предобработку. Например, преобразовать Input с использованием стандартной библиотеки и передать в конструктор Calculator то, что ближе ему в рамках реализации (т.е. более cohesive). Например, передать tokenStream вместо inputStream или что-то подобное. А иначе конструктор класса превращается в что-то менее потребное.

С отдельным методом можно производить более плавную эволюцию. Например, у нас добавили дополнительные настройки. Мы можем добавить новый метод, который принимает параметр. Потом вызвать новый метод из старого, задепрекейтить старый и постепенно (по-одному) заменить все использования со старого на новый. Можно еще тесты гонять по желанию после каждого изменения, чтобы знать, что конкретно поменялось. Т.е. будет у нас что-то вроде

public class Calculator {
  private final Input input;
  private Calculator(Input input) {
    this.input = input;
  }

  private Result calculateImpl() {
    ...
  }

  @Deprecated("use calculate(input, Options.defaultOptions())")
  public static Result calculate(Input input) {
    return calculate(input, Options.defaultOptions());
  }

  public static Result calculate(Input input, Options options) {
    if (!options.useFeatureA() && !options.useFeatureB())
      return new Calculator.calculate();
    DataStream ds =
      options.useFeatureA() ? new FancyStream(input.stream()) : input.stream();
    return new BetterImpl(options.useFeatureB().process(ds);
  }
}


Подобные вещи нормально смотрятся в функции. Через класс их тоже можно таскать. Но очень часто при подобной эволюции ответственность класса размывается. Он перестает быть сфокусирован на одной конкретной задаче и начинает работать с совершенно разными уровнями абстракций.

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

public interface MyCalculator {
  public Result calculate(Input input);
}

class MyProg {
  private final MyCalculator calc;

  MyProg(MyCalculator calc) {
    this.calc = calc;
  }

  void doSomething(Input input) {
    for (int i = 0; i < 10; i++) {
      calculator.calculate(input);
    }
  }
}


Потом мы пишем реализацию. Тут могут быть варианты. Если у нас уже есть "внешняя" реализация — можем просто делегировать к ней:

public class BasicCalculator implements MyCalculator {
  @override
  public Result calculate(Input input) {
    return Calculator.calculate(input);
  }
}


А может быть, реализация имеет высокую связность с классом пользователя и мы ее сделаем в том же классе реализации. Декораторами интерфейса можно добавить всякие красивости в виде логирования и метрик (и их можно потом использовать с другими реализациями логики калькулятора). Кстати, предыдущий код калькулятора не очень — сам тип BasicCalculator не несет никакой дополнительной функциональности (нет другого API кроме MyCalculator). Поэтому можно его создавать методом-конструктором. Или, в данном случае, статическим полем:

final class MyCalculators {
  private MyCalculators() {
    throw new UnsupportedOperationException("Why do you want this?");
  }

  /** 
   * A basic implementation of the calculator that just delegates all
   * computation to the standard library implementation Calculator.
   */
  public MyCalculator basicCalculator =
    new MyCalculator() {
      @override
      public Result calculate(Input input) {
        return Calculator.calculate(input);
      }
    }
}


Т.е. мы рассмотрели несколько задач:

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

Все вышенаписанное вообще должно делаться автоматически. Это стандартные проблемы дизайна со стандартными решениями и причинами таких решений. Там даже мозг можно не включать!

И вот здесь мы опять возвращаемся к одному большому коммиту гит. Так и получается, что очень много разработчиков не знают "технологический процесс" (кто там недавно писал про технологические карты? ). Они не видят возникающих в процессе написания стандартных вопросов и решений. Не могут выделить один или несколько "тесно связанных" задач, которые составят прекрасный полностью законченный коммит (такое не всегда возможно, но у вас в примере — вполне).
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.