Я работаю над большим сольным проектом и сейчас, и у меня есть несколько классов, в которых я не вижу причин для создания экземпляра.
Например, мой класс игры в кости прямо сейчас хранит все свои данные статически, и все его методы тоже статические. Мне не нужно инициализировать его, потому что, когда я хочу бросить кости и получить новое значение, я просто использую Dice.roll()
.
У меня есть несколько похожих классов, у которых есть только одна основная функция, подобная этой, и я собираюсь начать работать над своего рода «контроллерным» классом, который будет отвечать за все события (например, когда игрок движется и какой текущий ход). это так) и я обнаружил, что могу следовать той же идее для этого класса. Я никогда не планирую создавать несколько объектов для этих конкретных классов, так что было бы плохой идеей сделать их полностью статичными?
Мне было интересно, если это считается "плохой практикой", когда речь идет о Java. Из того, что я видел, сообщество, похоже, немного расколото по этой теме? В любом случае, я хотел бы обсудить это, и ссылки на ресурсы тоже были бы великолепны!
Ответы:
Нет ничего плохого в статических классах, которые действительно статичны . То есть не существует внутреннего состояния, о котором можно было бы говорить, что привело бы к изменению вывода методов.
Если
Dice.roll()
просто возвращается новое случайное число от 1 до 6, это не меняет состояние. Конечно, вы можете делитьсяRandom
экземпляром, но я бы не подумал, что изменение состояния, как по определению, вывод всегда будет хорошим, случайным. Это также потокобезопасный, поэтому здесь нет проблем.Вы часто будете видеть финальные "вспомогательные" или другие служебные классы, которые имеют приватный конструктор и статические члены. Закрытый конструктор не содержит логики и служит только для предотвращения создания экземпляра класса. Последний модификатор лишь возвращает идею, что это не тот класс, из которого вы бы хотели получить. Это просто служебный класс. Если все сделано правильно, не должно быть ни одного сингла или других учеников, которые сами не являются статичными и окончательными.
Пока вы следуете этим правилам и не делаете одиночные игры, в этом нет абсолютно ничего плохого. Вы упоминаете класс контроллера, и это почти наверняка потребует изменения состояния, поэтому я бы не советовал использовать только статические методы. Вы можете сильно полагаться на статический служебный класс, но вы не можете сделать его статическим служебным классом.
Что считается изменением состояния для класса? Что ж, давайте исключим случайные числа на секунду, так как они по определению недетерминированы и, следовательно, возвращаемое значение часто меняется.
Чистая функция - это функция, которая является детерминированной, то есть для данного входа вы получите один и ровно один выход. Вы хотите, чтобы статические методы были чистыми функциями. В Java есть способы настройки поведения статических методов для хранения состояния, но они почти никогда не являются хорошими идеями. Когда вы объявляете метод как статический , типичный программист сразу же предполагает, что это чистая функция. Отклонение от ожидаемого поведения - это то, как вы склонны создавать ошибки в вашей программе, и вообще этого следует избегать.
Синглтон - это класс, содержащий статические методы, которые настолько противоположны «чистой функции», насколько это возможно. Один статический закрытый член хранится внутри класса, который используется для обеспечения того, чтобы был только один экземпляр. Это не лучшая практика и может привести к неприятностям позже по ряду причин. Чтобы понять, о чем мы говорим, вот простой пример синглтона:
источник
Dice.roll()
не является допустимым исключением из правила «нет глобального состояния».random.nextInt(6) + 1
. ;)RollAndMove()
снова бросаемся, когда мы поставляем двойную шестерку, то самый простой и надежный способ сделать это - смоделировать либоDice
генератор случайных чисел. Ergo,Dice
не хочет быть статическим классом, использующим статический генератор случайных чисел.Чтобы привести пример ограничений
static
класса, что если некоторые из ваших игроков захотят получить небольшой бонус за бросок кубика? И они готовы платить большие деньги! :-)Да, вы можете добавить еще один параметр, поэтому
Dice.roll(bonus)
,Позже вам нужны D20s.
Dice.roll(bonus, sides)
Да, но у некоторых игроков есть умение «чрезвычайно способного», поэтому они никогда не могут «шарить» (бросьте 1).
Dice.roll(bonus, sides, isFumbleAllowed)
,Это становится грязным, не так ли?
источник
new Dice().roll(...)
должен быть принят статический доступ, а такжеDice.roll(...)
. Мы только выиграем , если впрыснуть эту зависимость.static
в каждом вызове возникает беспорядок , а необходимые знания требуются при каждом вызове, поэтому он распространяется по всему приложению. В структуре ООП только конструктор / фабрика должны быть грязными, а детали этого кристалла инкапсулированы. После этого используйте полиморфизм и просто позвонитеroll()
. Я мог бы отредактировать этот ответ, чтобы уточнить.Я думаю, что в конкретном случае класса Dice использование методов экземпляра, а не статики, значительно упростит тестирование.
Если вы хотите выполнить модульное тестирование чего-либо, использующего экземпляр Dice (например, класс Game), то из ваших тестов вы можете внедрить некоторую форму двойного теста Dice, которая всегда возвращает фиксированную последовательность значений. Ваш тест может проверить, что игра имеет правильный результат для этих бросков костей.
Я не Java-разработчик, но я думаю, что было бы значительно сложнее сделать это с полностью статическим классом Dice. См. Https://stackoverflow.com/questions/4482315/why-does-mockito-not-mock-static-methods
источник
На самом деле это известно как шаблон Monostate , где каждый экземпляр (и даже «без экземпляра») делится своим статусом. Каждый член является членом класса (то есть, нет членов экземпляра). Они обычно используются для реализации классов «инструментария», которые связывают набор методов и констант, связанных с одной ответственностью или требованием, но не нуждаются в состоянии для работы (они чисто функциональны). Фактически, Java поставляется с некоторыми из них в комплекте (например, Math ).
Немного не по теме: я редко согласен с именованием ключевых слов в VisualBasic, но в этом случае, я думаю,
shared
это, безусловно, яснее и семантически лучше (оно распределяется между самим классом и всеми его экземплярами), чемstatic
(остается после жизненного цикла области, в которой он объявлен).источник