Раньше мне нравились служебные классы, наполненные статическими методами. Они объединили вспомогательные методы, которые в противном случае стали бы причиной избыточности и адского обслуживания. Они очень просты в использовании, без создания экземпляров, без удаления, просто огонь и забыть. Думаю, это была моя первая невольная попытка создать сервис-ориентированную архитектуру - множество сервисов без сохранения состояния, которые просто выполняли свою работу, и ничего больше. Однако по мере роста системы появляются драконы.
Полиморфизм
Допустим, у нас есть метод UtilityClass.SomeMethod, который радостно гудит. Вдруг нам нужно немного изменить функционал. Большая часть функциональности осталась прежней, но, тем не менее, нам нужно изменить пару частей. Если бы это был не статический метод, мы могли бы создать производный класс и при необходимости изменить содержимое метода. Поскольку это статический метод, мы не можем. Конечно, если нам просто нужно добавить функциональность до или после старого метода, мы можем создать новый класс и вызвать внутри него старый, но это просто мерзко.
Проблемы с интерфейсом
Статические методы не могут быть определены через интерфейсы по логическим причинам. А поскольку мы не можем переопределить статические методы, статические классы бесполезны, когда нам нужно передать их через их интерфейс. Это лишает нас возможности использовать статические классы как часть шаблона стратегии. Мы можем исправить некоторые проблемы, передавая делегаты вместо интерфейсов.
Тестирование
Это в основном идет рука об руку с проблемами интерфейса, упомянутыми выше. Поскольку наши возможности по замене реализаций очень ограничены, у нас также будут проблемы с заменой производственного кода тестовым кодом. Опять же, мы можем заключить их в оболочку, но это потребует от нас изменения больших частей нашего кода, чтобы иметь возможность принимать оболочки вместо фактических объектов.
Размещает большие двоичные объекты
Поскольку статические методы обычно используются в качестве служебных, а служебные методы обычно имеют разные цели, мы быстро получим большой класс, заполненный некогерентной функциональностью - в идеале , каждый класс должен иметь единственную цель в системе. Я бы предпочел, чтобы классы были в пять раз больше, если их цели четко определены.
Подъем параметров
Начнем с того, что этот маленький симпатичный и невинный статический метод может принимать один параметр. По мере роста функциональности добавляется пара новых параметров. Вскоре добавляются дополнительные параметры, которые являются необязательными, поэтому мы создаем перегрузки метода (или просто добавляем значения по умолчанию на языках, которые их поддерживают). Вскоре у нас есть метод, который принимает 10 параметров. Действительно обязательны только первые три, параметры 4-7 необязательны. Но если указан параметр 6, необходимо также заполнить 7-9 ... Если бы мы создали класс с единственной целью делать то, что делал этот статический метод, мы могли бы решить эту проблему, взяв необходимые параметры в конструктор и позволяет пользователю устанавливать необязательные значения через свойства или методы для одновременной установки нескольких взаимозависимых значений. Кроме того, если метод вырос до такого уровня сложности, он, скорее всего, в любом случае должен быть в своем собственном классе.
Требование от потребителей создать экземпляр класса без всякой причины
Один из наиболее распространенных аргументов - почему требовать, чтобы потребители нашего класса создавали экземпляр для вызова этого единственного метода, не имея при этом никакого смысла. для экземпляра потом? Создание экземпляра класса - очень дешевая операция для большинства языков, поэтому скорость не является проблемой. Добавление дополнительной строчки кода для потребителя - это невысокая цена, позволяющая заложить основу для гораздо более удобного в обслуживании решения в будущем. И, наконец, если вы хотите избежать создания экземпляров, просто создайте одноэлементную оболочку вашего класса, которая позволяет легко повторно использовать - хотя это требует, чтобы ваш класс не имел состояния. Если он не без состояния, вы все равно можете создавать статические методы-оболочки, которые обрабатывают все, но при этом дают вам все преимущества в долгосрочной перспективе. Наконец, вы также можете создать класс, который скрывает создание экземпляра, как если бы он был синглтоном: MyWrapper.Instance - это свойство, которое просто возвращает new MyClass ();
Только ситхи разбираются в абсолютных вещах
Конечно, есть исключения из моей неприязни к статическим методам. Истинные служебные классы, не представляющие риска раздувания, являются отличным случаем для статических методов - например, System.Convert. Если ваш проект разовый и не требует обслуживания в будущем, общая архитектура на самом деле не очень важна - статическая или нестатическая, на самом деле не имеет значения - однако скорость разработки имеет значение.
Стандарты, стандарты, стандарты!
Использование методов экземпляра не мешает вам также использовать статические методы, и наоборот. При условии, что дифференциация обоснована и стандартизирована. Нет ничего хуже, чем смотреть на бизнес-уровень, разросшийся по разным методам реализации.
person
Mark S. Rasmussen
schedule
15.10.2008