Что-то не так с классом со всеми статическими методами? - PullRequest
45 голосов
/ 18 марта 2010

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

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

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

ОБНОВЛЕНИЕ : конкретное передаваемое состояние является набором результатов из базы данных. Класс отвечает за заполнение шаблона электронной таблицы Excel из набора результатов из БД. Я не знаю, имеет ли это какое-то значение.

Ответы [ 16 ]

21 голосов
/ 18 марта 2010

То, что вы описываете, - это просто структурированное программирование, как это может быть сделано в C, Pascal или Algol. В этом нет ничего плохого. В есть ситуации, в которых ООП является более подходящим, но ООП не является окончательным ответом, и если рассматриваемая проблема лучше всего подходит для структурированного программирования, тогда класс, полный статических методов, - это путь.

20 голосов
/ 18 марта 2010

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

Исходя из моего личного опыта, я работал над 100 приложениями KLOC, которые имеют очень глубокие объектные иерархии, все наследуют и переопределяют все остальное, все реализуют полдюжины интерфейсов, даже интерфейсы наследуют полдюжины интерфейсов, система реализует каждый шаблон дизайна в книге и т. д.

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

Иногда излишне усердное ООП приводит к тому, что классы настолько гранулированы, что это на самом деле чистый вред.

В отличие от этого, многие функциональные языки программирования, даже такие как OO, такие как F # и OCaml (и C #!), Поощряют плоскую и неглубокую иерархию. Библиотеки на этих языках обычно имеют следующие свойства:

  • Большинство объектов являются POCO или имеют не более одного или двух уровней наследования, где объекты представляют собой не более чем контейнеры для логически связанных данных.
  • Вместо классов, вызывающих друг друга, у вас есть модули (эквивалентные статическим классам), контролирующие взаимодействия между объектами.
  • Модули, как правило, работают с очень ограниченным числом типов данных и поэтому имеют узкую область применения. Например, модуль OCaml List представляет операции над списками, а модули Customer облегчают операции с клиентами. Хотя модули имеют более или менее ту же функциональность, что и методы экземпляров в классе, ключевое отличие библиотек на основе модулей состоит в том, что модули гораздо более автономны, гораздо менее детализированы и, как правило, имеют мало зависимостей от других модулей.
  • Обычно нет необходимости переопределять методы переопределения объектов подкласса, так как вы можете передавать функции как первоклассные объекты для специализации.
  • Хотя C # не поддерживает эту функцию, функторы предоставляют средства для подкласса специализированных модулей.

Большинство больших библиотек имеют тенденцию быть более широкими, чем глубокими, например, Win32 API, библиотеки PHP, библиотеки Erlang BIF, библиотеки OCaml и Haskell, хранимые процедуры в базе данных и т. Д. Таким образом, этот стиль программирования является боевым тестированием и, кажется, хорошо работать в реальном мире.

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

5 голосов
/ 18 марта 2010

Помогает ли перефразировать вопрос:

Можете ли вы описать данные, над которыми работают статические методы, как сущность, имеющую:

  • четкое значение
  • ответственность за поддержание согласованности внутреннего состояния.

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

4 голосов
/ 18 марта 2010

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

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

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

3 голосов
/ 18 марта 2010

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

Если мы говорим о классе с такими служебными методами, как Math.floor (), то это на самом деле не проблема. Но если класс является реальной зависимостью, например, объектом доступа к данным, то он связывает всех своих клиентов с его реализацией.

РЕДАКТИРОВАТЬ: Я не согласен с людьми, которые говорят, что «ничего плохого» в этом типе «структурированного программирования» нет. Я бы сказал, что такой класс, по крайней мере, пахнет кодом, когда встречается в обычном Java-проекте, и, вероятно, указывает на неправильное понимание объектно-ориентированного дизайна со стороны создателя.

3 голосов
/ 18 марта 2010

Мне кажется, что если класс должен поддерживать некоторую форму состояния (например, свойства), то его следует создать (например, "нормальный" класс.)

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

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

2 голосов
/ 22 марта 2010

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

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

2 голосов
/ 18 марта 2010

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

Одна вещь, которую следует учитывать в C #, - это то, что многие классы, ранее написанные полностью статическими, теперь могут рассматриваться как классы расширения .net, которые также являются сердцем статических классов. Многие расширения Linq основаны на этом.

Пример:

namespace Utils {
    public static class IntUtils        {
            public static bool IsLessThanZero(this int source)
            {
                return (source < 0);
            }
    }
}

Что затем позволяет просто сделать следующее:

var intTest = 0;
var blNegative = intTest.IsLessThanZero();
2 голосов
/ 18 марта 2010

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

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

2 голосов
/ 18 марта 2010

В этом шаблоне нет ничего плохого. На самом деле C # имеет конструкцию, называемую статическими классами, которая используется для поддержки этого понятия путем обеспечения требования, чтобы все методы были статическими. Кроме того, во фреймворке есть много классов, которые имеют эту функцию: Enumerable, Math и т. Д. *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...