Не использовать «статический» в C #?


109

Я отправил заявку, которую написал другим архитекторам, для проверки кода. Один из них почти сразу же ответил мне и сказал: «Не используйте« статические ». Вы не можете писать автоматические тесты со статическими классами и методами.« Статических »следует избегать».

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

Далее он упомянул кое-что, касающееся насмешек, методов IOC / DI, которые нельзя использовать со статическим кодом. Он говорит, что неудачно, когда сторонние библиотеки статичны из-за их непроверяемости.

Этот другой архитектор прав?

обновление: вот пример:

APIManager - этот класс содержит словари сторонних API, к которым я обращаюсь, в следующий раз. Он устанавливает ограничения на использование API, которые многие сторонние поставщики используют в своих условиях обслуживания. Я использую его везде, где я вызываю стороннюю службу, вызывая Thread.Sleep (APIManager.GetWait ("ProviderXYZ")); прежде чем сделать звонок. Все здесь является поточно-ориентированным и прекрасно работает с TPL в C #.


37
staticЭто хорошо; static поля должны быть обработаны очень осторожно
Марк Гравелл

2
Зачем ему писать тесты для сторонних библиотек? Разве вы обычно не тестируете свой собственный код, предполагая, что создатели сторонней библиотеки провели его тестирование?
Нет

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

8
Очевидно, у ваших коллег серьезное заболевание - острый системный ООПус-эритематоз. Им нужно лечение как можно скорее!
SK-logic

6
Есть раздел ответов для предоставления ответов, я не понимаю, почему люди комментируют в попытке дать кому-то ответ / предложение ... Это противоречит цели StackExchange, конечно, комментарий будет запрашивать дополнительную информацию.
Петески

Ответы:


121

Это зависит от того, поддерживают ли статические классы состояние или нет. Лично у меня нет проблем с функциями без сохранения состояния, слитыми вместе в статическом классе.


111
Верный. Статические функции, которые не имеют побочных эффектов, являются наиболее тестируемыми функциями из всех.
Роберт Харви

9
+1 к этому, но с условием безопасности: почти каждый абстрактный алгоритм не имеет состояния, но это не значит, что вы должны реализовать его как статический класс или метод без сохранения состояния. Реализация этого таким образом приводит к тому, что весь код, использующий его, вряд ли связан с ним. Это означает, например, что если вы реализуете алгоритм сортировки как статический метод и используете его в проекте - вы не сможете временно заменить сортировку другим алгоритмом, например, для целей тестирования и для сужения проблемной области. Это огромное препятствие во многих случаях. Кроме того, статический полностью нормально, если используется разумно.

11
Вкратце: они являются наиболее тестируемыми функциями, но не обязательно, что они делают другой код более тестируемым! Это то, что имел в виду архитектор, вероятно.

4
@ tereško: Язык C # требует, чтобы статические методы были частью статического класса, если вы не хотите создавать экземпляр класса для вызова метода. Возможно, вы имеете в виду «экземпляр», а не «класс».
Роберт Харви

8
@quetzalcoatl: совершенно законно передать делегат (т.е. другой алгоритм) статическому методу; все методы расширения в Linq являются статическими. Пример: msdn.microsoft.com/en-us/library/…
Роберт Харви

93

Он слишком общий по этому поводу. Он прав, это мешает тестированию. Однако staticклассы и методы имеют свое место, и это действительно зависит от контекста. Без примеров кода вы не можете сказать.

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

Это может быть серьезный запах кода. Для чего вы используете классы? Держать данные? Для доступа к базе данных? Тогда он прав. В этом случае вам следует обратить внимание на внедрение зависимостей, поскольку такой статический класс фактически является неявным синглтоном.

Если вы используете их для методов расширения или помощников, которые не изменяют состояние и просто работают с параметрами, которые вы предоставляете, то обычно это нормально.


3
+1 за упоминание о том, что это заявление было общим, и упоминание о методах расширения, которые также могут быть проверены, а зависимости, насколько я знаю, все еще можно высмеивать.
Нет

4
Да, статично, как экземпляр одного класса, как неявный синглтон, это будет грязно ...

"Для доступа к базе данных?" Почему нет ? если табличный адаптер не является статичным, тогда создается статический набор данных со строго типизированным классом @, в этом нет ничего плохого .... разве вы докажете свою точку зрения ссылкой или примером?
Математика

27

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

Лучшее, что можно сделать, это попробовать и юнит-тестировать свой код. Попробуйте разрабатывать повторяемые, независимые, простые тесты и тестировать только один метод за раз. Попробуйте запустить свои тесты в другом рандомизированном порядке. Можете ли вы получить стабильную «зеленую» сборку?

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


7
Это то, что меня поразило. Несколько статических и не имеющих состояния классов стилей «помощник» подойдут, но если у вас есть все 25% всех ваших классов в качестве статических, маловероятно, что ваши усилия будут ограничены этим случаем.
Мэтью Шарли

2
@MatthewScharley - он, вероятно, получил 4 класса, и один из них является статическим :)
Alexus

1
Обратите внимание, что если вы используете такие жесткие диски (например, синглтоны), то позднее создание большего количества экземпляров может оказаться сложной задачей. Как, например, создание приложения, которое обрабатывает документ, вы сталкиваетесь с проблемами позже, когда вы хотите управлять несколькими документами.
Мишель Кейзерс

11

Уже опубликованные ответы охватывают много действительно хороших моментов, но есть один, который, кажется, отсутствует:

Статические поля никогда не собираются мусором.

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

Статические функции не так плохи, но все остальные уже достаточно подробно рассмотрели это.


10

Одним из преимуществ, которые вы получаете от IoC / DI, является то, что большинство взаимодействий между классами согласовываются между интерфейсами. Это облегчает модульное тестирование, потому что интерфейсы могут быть смоделированы автоматически или полуавтоматически, и, следовательно, каждая из частей может быть проверена на входы и выходы. Кроме того, помимо тестов, размещение интерфейсов между всем позволяет получить максимально модульный код - вы можете легко заменить одну реализацию, не беспокоясь о том, что вы облажаете зависимости.

Поскольку в C # нет метаклассов, класс не может реализовать интерфейс с использованием статических функций, поэтому статические функции классов заканчивают усилия по реализации чистой объектной модели IoC / DI. То есть вы не можете создать макет для их проверки, и вам нужно создать реальные зависимости.

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


Commenter напоминает мне о методах расширения, которые, конечно, должны быть в статических классах в C #. Это законно и является примером того, как чистый IoC не очень хорошо работает в C #, по крайней мере, если вы пытаетесь воспользоваться широтой языка.


1
Если класс сделан статическим по правильным причинам и правильно реализованным, например, такими как методы расширения, они все еще очень тестируемы, поскольку не имеют внешних зависимостей и являются автономными, и поэтому не должны требовать проверки. Разве необходимость в интерфейсе не должна зависеть от требований к дизайну, чтобы наилучшим образом удовлетворить бизнес-требования, а не ради сохранения чистого проекта IoC / DI?
Нет

1
Вы и я согласны с общим мнением, что вы должны выбрать правильный инструмент для правильной работы, а не быть наручниками из-за философии. Однако, если в вашем проекте чистого IoC / DI существует устоявшаяся культура, это повредит так же, как преобразование традиционно нисходящего решения в IoC / DI. Инжиниринг должен учитывать переходные расходы. Между прочим, я не думаю, что методы расширения вполне могут помочь вам. Я думаю, что лучшим решением для поведения IoCish со статическими классами было бы выбрать несколько классов во время компиляции с директивами препроцессора, стиль C
jwrush

1
Er. Я тупой. Вы имели в виду статический класс для методов расширения HOLDING, что, конечно же, то, как вы должны их делать. Да, конечно, вам нужен статический класс для этой цели. Это пришло мне в голову: и это показывает, что C # не был предназначен для IoC. :)
jwrush

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

5

Статические классы иногда используются неправильно и должны быть:

  • singleton (где нестатический класс имеет статический член и общедоступную статическую переменную Instance для извлечения / создания его только один раз.
  • метод в другом классе (где государство имеет значение). Если у вас есть член, который не использует данные из этого класса, вероятно, он не должен быть частью этого класса.

Это также может быть так называемая «служебная» функция и часть служебного класса. Служебные классы - это классы, которые содержат только статические методы и служат вспомогательными функциями без какого-либо контекста.


@topomorto Потому что экземпляр класса должен создаваться не его конструктором, что приводит к нескольким объектам / экземплярам, ​​а специальным методом (обычно instance ()) <, который всегда возвращает один и тот же экземпляр, экземпляр которого создается после первого вызова instance ().
Мишель Кейзерс

@topomorto Я только что проверил, и вы совершенно правы, только некоторые члены являются статическими (переменная экземпляра и функция экземпляра). Я изменю свой ответ соответственно; спасибо за упоминание.
Мишель Кейзерс

4

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


1

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

Пример PHP:

class vendorData {
  private static $data_table = 'vendor_data'; // static property
  private $id; // instance property
  private $name; // instance property

  public function __construct($vendor_id) {
    if(!self::validId($vendor_id) ) { // static method
      return false; // id doesn't exist
    }
    $this->id = $vendor_id; // id has been validated
    $this->getProperties(); // object method
  }

  private static function validId($vendor_id) {
    // send db query to find if the id exists
    // return true/false;
  }

  private function getProperties() { // object method
    $sql = "SELECT * FROM `{self::$data_table}` // using static property
        WHERE `id` = {$this->id}"; // using object instance property
    // get resultset
    foreach($result as $property => $value) {
      $this->$property = $value; // object instance properties all set
    }
  }

  // and here
  public static function getBy($property,$value) { // static method to return object array
    $sql = "SELECT `id` FROM `{self::$data_table}` // using static property
      WHERE `$property` = '$value'";
    // send query, get $ids array
    $obj_array = array();
    foreach($ids as $id) {
      // create array of current class objects using special static keyword
      // meaning of static here is different than in property/method declarations
      $obj_array[$id] = new static($id);
    }
    return $obj_array;
  }
}

1

Я скажу, что принципы, которые у них есть, в порядке, но утверждение (не используйте статику) может быть ошибочным. Сколько стоит покрытие вашего кода? если число велико и вы устраиваете модульное тестирование своего приложения, то все в порядке. Если нет, то я бы предложил пересмотреть код. Вы можете найти статические классы или нет, это будет зависеть от многих вещей.


-3

Классы со статическими методами злоупотребляют принципами ООП. Это уже не ООП, это COP - классно-ориентированное программирование.

Они редко имеют четкую «личность» (я использую мою любимую человеческую метафору ) или идентичность. Поэтому они не знают, кто они. Одного этого момента достаточно, чтобы избавиться от этой концепции в ООП, но их больше.

Следующий является следствием первого пункта. Так как такие классы не знают, кто они, они имеют тенденцию быть от большого до огромного.

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

Медленно, но неизбежно, ваш код становится все менее и менее связным, поскольку скрытые зависимости плохо контролируются. Они кажутся невидимыми. И это так легко добавить еще один! Вам не нужно изменять подпись любого метода. Все, что вам нужно сделать, это вызвать статический метод. И какой ужасный код следует ...

Хорошо, вы, наверное, уже догадались. Плотное сцепление часто сопровождает низкую сплоченность. Если есть много клиентов, использующих какой-то класс, то связь становится более жесткой. И есть большой соблазн в использовании уже написанного класса или метода, который требует лишь небольшого исправления. Только один метод флаг-параметр.

Что использовать вместо статических методов? Ну, мое предложение ООП. Почему бы не попробовать?

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.