Сделать методы, которые не зависят от полей экземпляра, статическими?

19

Недавно я начал программировать в Groovy для инфраструктуры тестирования интеграции, для проекта Java. Я использую Intellij IDEA с плагином Groovy и с удивлением вижу в качестве предупреждения все методы, которые не являются статичными и не зависят от каких-либо полей экземпляра. В Java, однако, это не проблема (по крайней мере, с точки зрения IDE).

Должны ли все методы, которые не зависят ни от каких полей экземпляров, быть преобразованы в статические функции? Если это правда, это специфично для Groovy или доступно для ООП вообще? И почему?

m3th0dman
источник
Эти методы вызывают другие методы экземпляра? Или они буквально не имеют ничего общего с объектом этого класса? Можете привести пример?
Рэй Тоал

Ответы:

26

Обратите внимание, что IDEA имеет эту проверку и для Java, она называется Method может быть «статической» ,

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

Дело в том, что для кода Java эта проверка по умолчанию отключена (программист может включить ее по своему усмотрению). Причина этого, скорее всего, заключается в том, что обоснованность / полезность такой проверки может быть оспорена на основе нескольких достаточно авторитетных источников.

Начнем с того, что официальное руководство по Java довольно ограничено, когда методы должны быть статичными:

Обычное использование статических методов - доступ к статическим полям.

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

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

Смотрите, например , Java World статьи - г - н Счастливый Объект учит статические методы :

Любой метод, который не зависит от состояния экземпляра, является кандидатом на то, чтобы быть объявленным как статический.

Обратите внимание, что я говорю «кандидат на то, чтобы быть объявленным как статический». Даже в предыдущем примере ничто не заставляет вас объявлять instances()как статические. Объявление его как статического просто делает его более удобным для вызова, так как вам не нужен экземпляр для вызова метода. Иногда у вас будут методы, которые не зависят от состояния экземпляра. Возможно, вы не захотите делать эти методы статичными. На самом деле вы, вероятно, захотите объявить их как статические, если вам нужен доступ к ним без экземпляра.

Более того, даже если вы можете объявить такой метод как статический, вы, возможно, не захотите этого из-за проблем наследования, которые он вставляет в ваш дизайн. Взгляните на «Эффективный объектно-ориентированный дизайн», чтобы увидеть некоторые проблемы, с которыми вы столкнетесь ...

В статье в блоге Google для тестирования даже говорится о том, что статические методы - это смерть для тестирования :

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

Иногда статические методы - это фабрика для других объектов. Это дополнительно обостряет проблему тестирования. В тестах мы полагаемся на тот факт, что мы можем по-разному связывать объекты, заменяя важные зависимости фиктивными. После вызова newоператора мы не можем переопределить метод подклассом. Вызывающий объект такой статической фабрики постоянно связан с конкретными классами, которые генерирует статический фабричный метод. Другими словами, повреждение статического метода намного выше самого статического метода. Вставка связывания графов объектов и конструкционного кода в статический метод очень плоха, так как связывание графов объектов - это то, как мы изолируем вещи для тестирования ...


Видите ли, приведенное выше выглядит вполне естественно, что упомянутая проверка отключена по умолчанию для Java.

Разработчикам IDE было бы очень трудно объяснить, почему они считают, что это так важно, чтобы установить его по умолчанию, вопреки широко признанным рекомендациям и лучшим практикам.

Для Groovy все совсем иначе. Ни один из приведенных выше аргументов не применим, в частности, тот, что касается тестируемости, как объяснено, например, в статье Mocking Static Methods в статье Groovy в Javalobby:

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

Это различие, вероятно, почему настройка по умолчанию для упомянутой проверки противоположна в Groovy. В то время как в Java по умолчанию «on» будет источником путаницы для пользователей, в Groovy противоположная настройка может сбить пользователей с IDE.

«Эй, метод не использует поля экземпляра, почему вы не предупредили меня об этом?» На этот вопрос было бы легко ответить для Java (как описано выше), но для Groovy просто нет убедительных объяснений.

комар
источник
Кстати, ReSharper (сделанный JetBrains для C # / Visual Studio) предлагает то же самое
Конрад Моравский,
6
Существует важное различие между статическими методами с побочными эффектами и без них. Извлечение Google действительно обращается к первому.
assylias
@assylias Да, но также обсуждается, как фабричные методы могут усложнить тестирование (поскольку оно тесно связывает зависимости в приложении). Использование инфраструктуры внедрения зависимостей является одним из лучших обходных путей для этого, а также решает многие другие проблемы.
По Лундбергу
5

Трудно представить, что это окажет какое-либо заметное влияние на производительность. Единственное преимущество, которое я вижу в их создании, staticзаключается в том, что он обеспечивает визуальную индикацию того, что состояние экземпляра не изменяется. Другими словами, он говорит человеку, читающему исходный код, что-то ... «интересное» об этом методе, просто благодаря тому, что он там есть. Реальный вопрос, это проясняет или смущает? «Почему этот метод статический? Это фабричный метод? Нужна ли возможность вызывать его без экземпляра объекта?»

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

Роберт Харви
источник
5
Учтите, что (в Java) статический метод не может быть переопределен. Хотя это не часто проблема, это означает, что некоторые подклассы позже не смогут изменить эту реализацию. Это не значит, что статический метод не является правильным ответом, а скорее является частью конструктивных соображений.
2
@ user40980 Стоит задуматься, правда, но также стоит учитывать, что наследование может быть неуклюжим инструментом, который часто используется не по назначению, и некоторые видные деятели утверждают, что все классы должны быть finalили специально разработаны с учетом наследования.
Сара
3

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

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

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

Ян
источник
-1

Обратите внимание, что у вас могут быть классы без состояния, которые реализуют некоторый интерфейс (например, java.lang.Runnableвы не можете сделать их методы статичными. Некоторые шаблоны (например, Стратегия) также могут создавать объекты без состояния, которые могут даже иметь несколько методов).

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

Евгений
источник
как это отвечает на заданный вопрос?
комнат
1
почему трудно перейти из какого-либо штата в какой-то штат? Я бы сказал, что все наоборот. легче загрязнить что-то чистое, чем очистить что-то загрязненное.
сара