Как решить статические и нестатические методы С#?

[изменить]

Мой оригинальный вопрос: "Зачем выбирать между статическими и нестационарными? Оба делают то же самое..."

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

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

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

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

Document.Validate(myDocumentObject);    

но также

myConcreteDocumentObject.Validate();

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

myConcreteDocument.Copy(toPath);

но также

Document.Copy(myConcreteDocumentObject, toPath)

или

Document.Copy(fromPath, toPath)

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

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

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

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

Например:

myDocumentManagerObject.Copy(myConcreteDocumentObject, toPath);

или

myDocumentManagerObject.Copy(myConcreteDocumentObject, toPath);

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

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

[/EDIT]


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

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

(Хотя я использую С# -Syntax, это не проблема с С#)

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

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) и придерживайтесь его, чтобы не ослабить интерфейс моего документа-класса?

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

Class.Function(aConcreteClassObject, parameters);

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

aConcreteObject.DoSomething(parameters);

Чтобы дать реальный пример:

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

//----- 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, так как это более общий вопрос о хорошем дизайне классов.)

Ответ 1

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

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

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

Есть и другие важные моменты:

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

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

Ответ 2

Здесь мы идем.

Прежде всего:

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

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

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

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

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


Ответ 3

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

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

interface IDocument 
{
   void Print(IDevice targetDevice);
}

IDocument instance;

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

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

Ответ 4

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

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

Ответ 5

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

Document.Copy(myDocumentObject, toPath);

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

Ответ 6

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

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

document1.copy(toPath)

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

Document.copy(fromPath, toPath)

Ответ 7

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

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

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

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

  • Если метод или класс о метаинформации статичны.

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

Ответ 8

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

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

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 вместе, что очень сложно проверить и поддерживать.

Ответ 9

В общем, я бы сказал, что "копирование", насколько это касается объекта, обычно означает клонирование одной информации в новый объект. Описанное здесь "копирование" - это то, что файловая система делает от вашего имени, а не объекта. Таким образом, я бы сделал его статическим методом, а не методом экземпляра документа.

Ответ 10

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

Ответ 11

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

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

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

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