Как выбрать между статическими и нестатическими методами в C #? - PullRequest
14 голосов
/ 28 апреля 2009

[Изменить]

Мой оригинальный вопрос был «Почему нужно выбирать между статическим и нестатичным? Оба делают то же самое ...»

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

Итак, позвольте мне сделать несколько дополнений:

Когда я говорю "интерфейс", я имею в виду не интерфейс C # -ключей, а то, что я понимаю, что-то вроде интерфейса С ++: набор четко определенных функций для работы с моим объектом. Говоря об ослаблении моего интерфейса, я имею в виду разные функции (статические / нестатические), которые выполняют одно и то же. Мой интерфейс больше не определен, когда есть разные функции для выполнения одной и той же вещи.

Итак, как написал Боб Джанитор, я могу реализовать функцию Validate () -

Document.Validate(myDocumentObject);    

но также

myConcreteDocumentObject.Validate();

Чтобы вернуться к моему Copy () - например, можно реализовать Copy () как

myConcreteDocument.Copy(toPath);

но также

Document.Copy(myConcreteDocumentObject, toPath)

или

Document.Copy(fromPath, toPath)

когда я думаю о папке, которая содержит все файлы, принадлежащие моему Документу (в этом случае я не зависим от конкретного экземпляра - но я зависим от других вещей:)).

В общем, я говорю о статических методах, а не о статических классах (извините, если я забыл упомянуть).

Но, как сказал Антон Гоголев, я думаю, что мой класс по Документам не является хорошим примером и не очень хорошо продуман, поэтому я думаю, что мне придется взглянуть на Принцип Единой Ответственности.

Я также мог бы реализовать какой-то класс ManagerClass, который работает с моим DocumentClass:

Например:

myDocumentManagerObject.Copy(myConcreteDocumentObject, toPath);

или

myDocumentManagerObject.Copy(myConcreteDocumentObject, toPath);

но если я обращаюсь к подходу 1), я склонен создавать объекты, которые выполняют свои задачи самостоятельно, а не другие объекты (DocumentManager), которые делают что-то с моим DocumentObject.

(Надеюсь, это не пойдет на сторону религиозной дискуссии об ООП;).)

[/ EDIT]


Старая версия:

Сначала кажется, что это очень простой вопрос, например, «когда использовать статические методы, а когда нет», но с этим я время от времени сталкиваюсь (и мне трудно описать, в чем заключается настоящая проблема; возможно, это просто чтобы получить причины, почему (не) использовать 1) или почему (не) использовать 2)).

(хотя я использую синтаксис C #, это не проблема C #)

В ООП существует два подхода (среди прочих) к работе с объектами:

1) Если я хочу, чтобы мой объект что-то сделал, я просто говорю ему сделать это:

myConcreteObject.DoSomething();

Это как разговаривать с объектом.

2) Или, если вы поклонник статических методов:

ObjectClass.JustDoIt();

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

Итак, при разработке класса мне часто приходится решать, использовать ли мне подход 1) или подход 2):

Представьте, что у вас есть класс «Документ», который должен обозначать документ, который должен быть сохранен в базе данных:

Документ

  • состоит из одного или нескольких файлов изображений из файловой системы (они становятся страницами одного документа)
  • имеет что-то вроде библиографии - поля, в которые пользователь может добавлять информацию о документе, которая сохраняется в дополнительном файле
  • и должен иметь некоторые операции, такие как Copy (), AddPage (), RemovePage () и т. Д.

Теперь я сталкиваюсь с несколькими способами создания этого класса:

//----- 1) non static approach/talking to objects -----
Document newDocument = new Document();

// Copy document to x (another database, for example)
newDocument.Copy(toPath);

Мне нравится это: я приказываю документу скопировать себя в базу данных x, и объект делает это сам. Ницца.

//----- 2) static approach ----------------------------
Document.Copy(myDocumentObject, toPath);

Почему бы и нет? Также приятно, очень удобно ...

Так, что реализовать? И то и другое? Или поместить статический подход в своего рода вспомогательный класс? Или выбрать подход 1) и придерживаться его, чтобы не ослабить интерфейс моего Document-класса?

Размышляя об обоих подходах, я прихожу к выводу, что (теоретически) можно реализовать любую функцию как статическую функцию:

Class.Function(aConcreteClassObject, parameters);

но также нестатично:

aConcreteObject.DoSomething(parameters);

Чтобы привести пример из реальной жизни:

[EDIT (добавлен параметр из пути "Извините, я забыл")]

//----- 2) static approach ----------------------------
File.Copy(fromPath, toPath);    // .Net-Framework-like

[/ EDIT]

но также:

//----- 1) non static approach ------------------------
ExampeFileClass fileObject = new ExampleFileClass();
fileObject.Copy(toPath);

или даже (вид OOP-Overkill):

//----- 1) non static approach, too -------------------
fileObject.ToPath = @"C:\Test\file.txt";     // property of fileObject
fileObject.Copy();                           // copy to toPath

Итак, почему (не) использовать 1) или почему (не) использовать 2)?

(Я бы не стал слишком концентрироваться на примере класса Document, так как это более общий вопрос о хорошем дизайне класса.)

Ответы [ 11 ]

16 голосов
/ 28 апреля 2009

Вот и мы.

Сначала:

Поэтому я склонен использовать статические методы очень часто (быть независимым от конкретного экземпляра - независимость всегда полезна).

Совсем наоборот: при использовании статических методов вы очень зависите от конкретного экземпляра.

Что касается вашего Document, я бы не пошел никуда. Вы перечислили все обязанности класса Document, который включает в себя агрегацию данных, сохранение себя в базе данных, а также операции на страницах и копирование.

Это способ многого. В соответствии с SRP каждый «модуль» (здесь «модуль» используется как универсальный термин) должен иметь только одну причину для изменения. У вашего Document много обязанностей, поэтому у него есть масса причин для изменения. Это не хорошо.

Имея это в виду, я бы переместил всю логику в другие классы со строго определенными обязанностями. Я полагаю, более или менее принятый критерий того, что перемещать, был введен Хербом Саттером или Андреем Александреску следующим образом: все операции (методы мышления), которые могут быть выполнены с объектом через его публичный контракт, должны быть перенесены за пределы. рассматриваемый объект.


9 голосов
/ 28 апреля 2009

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

Подумайте, как бы вы реализовали следующую функциональность, используя только статические методы?

interface IDocument 
{
   void Print(IDevice targetDevice);
}

IDocument instance;

instance = new PdfDocument();
instance.Print(printer);

instance = new WordDocument();
instance.Print(printer);
8 голосов
/ 28 апреля 2009

ПОЦЕЛУЙ. Если вам не нужно вызывать конструктор, даже лучше.

Кроме того, статический метод должен немного рассказать вам о том, как работает функция:

  • Он не работает с переменными вне того, что ему передано.
  • Не требуется никакой памяти, кроме как при вызове метода (не считая того, что возвращается из функции)

Есть еще несколько важных вещей, на которые стоит обратить внимание:

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

Я бы также сослался на эту ветку и простой поиск в Google , который откровенно обеспечивает обильное обсуждение этой темы.

6 голосов
/ 28 апреля 2009

Мое "правило":

  • Если мне не нужно использовать свойства из моего класса, сделайте его статическим. (другими словами, если метод на самом деле не привязан к классу, просто для логической ассоциации используйте static)
3 голосов
/ 28 апреля 2009

В общем, если у вас есть такой метод, как:

Document.Copy(myDocumentObject, toPath);

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

2 голосов
/ 13 мая 2009

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

Ваш пример, рассказывающий о классе Document с методом copy, является ярким примером. Я бы сказал, что правильная реализация ОО - это первый путь . То есть иметь копию как метод экземпляра, подобный этому:

document1.copy(toPath)

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

Document.copy(fromPath, toPath)
1 голос
/ 28 апреля 2009

Статические методы могут быть очень полезны, я люблю методы расширения, но они заставляют связываться и при неправильном использовании могут сделать тестирование кошмаром!

Хороший пример использования статики - это когда вы хотите сделать проверку

public static errors Validate(Document myDoc)
{
..some validation code
}

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

.
public static errors ValidateAndSave(Document myDoc)
{
    errors docErrors = Validate(myDoc);
    if(docErrors.count==0)
    {
         docErrors = SaveToDB(myDoc);
    }

   return docErrors; 
} 

Это настоящая боль в тестировании, потому что каждый раз, когда вы запускаете его, и он проходит проверку вашего переноса в базу данных, ваша логика Biz может не выдавать ошибку, но ваш уровень DAL может, вместо того чтобы тестировать только слой Biz, который вам также необходим для тестирования слоя DAL, и ваша тесная связь между вашим объектом, вашим слоем Biz и вашим Dal, что делает его очень трудным для тестирования и обслуживания.

1 голос
/ 28 апреля 2009

Если вам нужно спросить, не используйте статику.

Фактическое эмпирическое правило (и существует множество реальных технических причин, но я считаю, что это помогает объяснить концепции):

  • Если рассматриваемый класс может существовать несколько раз, он не является статичным.

  • Если рассматриваемый метод действует против информации об экземпляре, он не является статическим.

  • Если метод или класс связан с метаинформацией, он является статическим.

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

0 голосов
/ 29 апреля 2009

Если вы используете какие-либо другие объекты, то по умолчанию вы используете методы уровня экземпляра, чтобы вы могли настроить эти зависимости с помощью Dependancy Injection.

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

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

Полезная «красная черта» заключается в том, что если вы коснетесь другого процесса (сервера базы данных, веб-службы и т. Д.), То я буду считать статический метод плохим в 100% случаев, поскольку это делает модульное тестирование более сложным.

0 голосов
/ 28 апреля 2009

То же, что и altCongnito, и я добавлю этот файлObject.Copy, который будет использовать каждый, больше, чем объект fileObject. Статика для функции, которая имеет идеальные отношения с классом, а не функциональную зависимость от него.

...