Если вы используете ключевое слово «static» без ключевого слова «final», это должно быть сигналом к тщательному рассмотрению вашего дизайна.Даже наличие 'final' не является свободным проходом, так как изменяемый статический конечный объект может быть столь же опасным.
Я бы оценил где-то в 85% случаев, когда я вижу "static" без«окончательный», это НЕПРАВИЛЬНО.Часто я нахожу странные обходные пути, чтобы маскировать или скрывать эти проблемы.
Пожалуйста, не создавайте статические изменяемые файлы.Особенно Коллекции.В общем, Коллекции должны быть инициализированы, когда инициализируется содержащий их объект, и должны быть спроектированы таким образом, чтобы их сбрасывали или забывали о том, что их содержащий объект забыт.
Использование статики может привести к очень тонким ошибкам, которые вызовут поддержку инженеровдни боли.Я знаю, потому что я и создавал, и охотился за этими ошибками.
Если вы хотите получить более подробную информацию, пожалуйста, прочитайте…
Почему бы не использовать статику?
Есть много проблем со статикой, включая написание и выполнение тестов, а также тонкие ошибки, которые не сразу очевидны.
Код, основанный на статических объектах, не может быть легко проверен модулем, и статикане может быть легко издеваться (обычно).
Если вы используете статику, невозможно поменять реализацию класса, чтобы протестировать компоненты более высокого уровня.Например, представьте статический CustomerDAO, который возвращает объекты Customer, которые он загружает из базы данных.Теперь у меня есть класс CustomerFilter, которому нужен доступ к некоторым объектам Customer.Если CustomerDAO статический, я не могу написать тест для CustomerFilter без предварительной инициализации моей базы данных и заполнения полезной информации.
А заполнение базы данных и ее инициализация занимают много времени.По моему опыту, ваша структура инициализации БД со временем будет меняться, а это значит, что данные будут изменяться, и тесты могут прерваться.IE, представьте, что Customer 1 раньше был VIP, но структура инициализации БД изменилась, и теперь Customer 1 больше не VIP, но ваш тест был жестко запрограммирован для загрузки Customer 1…
. Лучший подход - этосоздать экземпляр CustomerDAO и передать его в CustomerFilter при его создании.(Еще лучшим подходом было бы использование Spring или другой платформы Inversion of Control.
После того, как вы это сделаете, вы можете быстро смоделировать или заглушить альтернативный DAO в вашем CustomerFilterTest, что позволит вам лучше контролироватьtest,
Без статического DAO тест будет более быстрым (без инициализации db) и более надежным (потому что он не потерпит неудачу при изменении кода инициализации db). Например, в этом случае обеспечение Customer 1является и всегда будет VIP, если речь идет о тесте.
Выполнение тестов
Статика вызывает реальную проблему при одновременном запуске комплектов модульных тестов (дляНапример, с вашим сервером Continuous Integration.) Представьте статическую карту сетевых объектов Socket, которая остается открытой от одного теста к другому. Первый тест может открыть Socket на порту 8080, но вы забыли очистить карту, когда тест разрываетсявниз. Теперь, когда запускается второй тест, он может произойти сбой при попытке создать новый сокетдля порта 8080, так как порт все еще занят.Представьте также, что ссылки на сокеты в вашей статической коллекции не удаляются и (за исключением WeakHashMap) никогда не могут быть подвергнуты сборке мусора, что приводит к утечке памяти.
Это слишком обобщенный пример, но вВ больших системах эта проблема возникает ВСЕ ВРЕМЯ.Люди не думают о модульных тестах, запускающих и останавливающих свое программное обеспечение повторно в одной и той же JVM, но это хороший тест вашего программного обеспечения, и если у вас есть стремление к высокой доступности, это то, о чем вы должны знать.
Эти проблемы часто возникают прикаркасные объекты, например, уровни доступа к БД, кэширования, обмена сообщениями и ведения журнала.Если вы используете Java EE или некоторые из лучших в своем роде фреймворков, они, вероятно, справятся с этим для вас, но если вы, как и я, имеете дело с устаревшей системой, у вас может быть множество пользовательских фреймворков для доступа к этим слоям.
Если конфигурация системы, которая применяется к этим компонентам инфраструктуры, изменяется между модульными тестами, а среда модульного тестирования не разрушает и не перестраивает компоненты, эти изменения не вступают в силу, и когда тест полагается на эти изменения, они потерпят неудачу.
Даже не-каркасные компоненты подвержены этой проблеме.Представьте себе статическую карту под названием OpenOrders.Вы пишете один тест, который создает несколько открытых ордеров, и проверяет, чтобы убедиться, что все они находятся в правильном состоянии, затем тест завершается.Другой разработчик пишет второй тест, который помещает необходимые ему заказы в карту OpenOrders, а затем утверждает, что количество заказов является точным.Выполненные по отдельности, эти тесты оба пройдут, но при совместном запуске в комплекте они не пройдут.
Хуже того, сбой может быть основан на порядке, в котором выполнялись тесты.
Вв этом случае, избегая статики, вы избегаете риска сохранения данных во всех тестовых экземплярах, обеспечивая лучшую надежность теста.
незначительные ошибки
Если вы работаете в среде высокой доступностиили в любом месте, где потоки могут быть запущены и остановлены, та же проблема, упомянутая выше с наборами модульных тестов, может применяться, когда ваш код также работает в рабочей среде.
При работе с потоками вместо использования статического объекта дляДля хранения данных лучше использовать объект, инициализированный на этапе запуска потока.Таким образом, каждый раз, когда поток запускается, создается новый экземпляр объекта (с потенциально новой конфигурацией), и вы избегаете передачи данных из одного экземпляра потока в следующий экземпляр.
Когдапоток умирает, статический объект не сбрасывается или сборщик мусора.Представьте, что у вас есть поток с именем «EmailCustomers», и когда он запускается, он заполняет статическую коллекцию строк списком адресов электронной почты, а затем начинает отправлять электронные письма на каждый из адресов.Допустим, поток каким-то образом прерван или отменен, поэтому среда высокой доступности перезапускает поток.Затем, когда поток запускается, он перезагружает список клиентов.Но поскольку коллекция является статической, она может сохранить список адресов электронной почты из предыдущей коллекции.Теперь некоторые клиенты могут получать дубликаты электронных писем.
В стороне: статический финал
Использование «статического финала» фактически является Java-эквивалентом C #define, хотяСуществуют технические различия в реализации.AC / C ++ #define выгружается из кода препроцессором перед компиляцией.Java «статический финал» в конечном итоге останется в стеке.Таким образом, она больше похожа на переменную «static const» в C ++, чем на # define.
Резюме
Надеюсь, это поможет объяснитьнесколько основных причин, по которым статика проблематична.Если вы используете современную среду Java, такую как Java EE или Spring и т. Д., Вы можете не сталкиваться со многими из этих ситуаций, но если вы работаете с большим объемом устаревшего кода, они могут стать гораздо более частыми.