Разве я не могу использовать все статические методы?

64

В чем разница между двумя методами UpdateSubject ниже? Я чувствовал, что использование статических методов лучше, если вы просто хотите оперировать сущностями. В каких ситуациях мне следует использовать нестатические методы?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

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

Становится ли это непрактичным, когда речь идет о наследовании?

Обновление: это
происходит на нашем рабочем месте сейчас. Мы работаем над 6-месячным веб-приложением asp.net с 5 разработчиками. Наш архитектор решил, что мы используем все статические методы для всех API. Его рассуждения о том, что статические методы легки, и приносят пользу веб-приложениям, снижая нагрузку на сервер.

Александр
источник
9
Rich Hickey будет поощрять что-то не идиоматическое подобное (все статические методы). Если вам нравится функциональный стиль, вы также можете использовать функциональный язык;) Не все в программном обеспечении лучше всего моделировать как объект.
Работа
13
@Job: я действительно не вижу, как это функционально - это процедурно.
Ааронаут
2
@Job: этот класс не является неизменным.
Аарона
3
@DonalFellows: Конечно, вы можете, C # и F # оба смешанные парадигмы. Но факт остается фактом, статические методы! = Функциональное программирование. FP означает функции передачи / связывания, неизменные типы данных, избежание побочных эффектов и т. Д. Это полностью противоположно тому, что делает фрагмент кода в OP.
Aaronaught
4
«Его рассуждения о том, что статические методы легки и полезны для веб-приложений, снижая нагрузку на сервер». Это не правильно. Снизить нагрузку на сервер?
mamcx

Ответы:

87

Я пойду с наиболее очевидными проблемами статических методов с явными параметрами "this":

  1. Вы теряете виртуальную диспетчеризацию и впоследствии полиморфизм. Вы никогда не сможете переопределить этот метод в производном классе. Конечно, вы можете объявить метод new( static) в производном классе, но любой код, который обращается к нему, должен знать всю иерархию классов и выполнять явную проверку и приведение, чего в точности следует избегать ОО .

  2. Вроде расширения # 1, вы не можете заменить экземпляры класса интерфейсом , потому что интерфейсы (в большинстве языков) не могут объявлять staticметоды.

  3. Ненужное многословие. Что более читабельно: Subject.Update(subject)или просто subject.Update()?

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

  5. Это сбивает с толку. Когда нормальный, разумный программист видит staticметод, он, естественно, предполагает, что ему не требуется допустимый экземпляр (если только он не принимает несколько экземпляров, например, метод сравнения или равенства, или ожидается, что он сможет работать со nullссылками) , Вид статических методов, используемых таким образом, заставит нас сделать двойной или, возможно, тройной дубль, и после 4-го или 5-го раза мы будем испытывать стресс и злость, и Бог поможет вам, если мы узнаем ваш домашний адрес.

  6. Это форма дублирования. Когда вы вызываете метод экземпляра, на самом деле происходит то, что компилятор или среда выполнения ищет метод в таблице методов типа и вызывает его, используя thisв качестве аргумента. Вы в основном заново реализуете то, что компилятор уже делает. Вы нарушаете DRY , повторяете один и тот же параметр снова и снова разными способами, когда это не нужно.

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

Aaronaught
источник
5
+1 за вашу последнюю строчку в одиночку. Я хотел бы, чтобы больше людей подумали «метод экземпляра», прежде чем они думают «статический метод».
Стюарт Лейланд-Коул
4
+1. Я бы добавил, что статические методы затрудняют написание тестов, так как в большинстве случаев вы не можете имитировать их: если какая-то часть вашего кода зависит от статического метода, вы не можете заменить его на «нет». оп в ваших тестах. Ярким примером в мире .NET могут быть классы, подобные File, FileInfoи DirectoryInfo(и на самом деле, есть библиотеки, которые скрывают их за интерфейсами, которые затем могут вводиться по мере необходимости и проверяться в тестах).
см
Я думаю, что ваши точки зрения на полиморфизм / наследование спорны. не следует использовать наследование для повторного использования кода, так что это не имеет значения. наследственная часть также сомнительна. в большинстве современных языков сигнатуры методов сами по себе полиморфны. если вы выбираете функциональный подход, вы начинаете передавать методы и составлять сложные методы из простых, которые взаимозаменяемы на основе их интерфейса. нет никаких недостатков в статических методах, которые не компенсируются простым проектированием с их учетом, как и в ООП.
Сара
@ См, это просто не правда. если у вас есть жесткая зависимость от НИЧЕГО, статического или нет, которое не может быть в модульном тесте, то это непроверенный период. статика не вызывает этого. статический метод может быть введен так же, как класс, представляющий соединение БД. вы предполагаете, что «статический» означает «статический плюс жесткие скрытые зависимости, затрагивающие глобальное состояние и внешние ресурсы». это не честно.
Сара
@kai попробуй ввести статический метод, который делает Y вместо X, какими бы ни были X и Y. Вам не нужно брать зависимости от чего-либо внешнего, чтобы быть прикрученным. Может быть, вы просто заинтересованы в тестировании некоторого аспекта, который не требует длительных вычислений X для выполнения. Как внедрить ваш модный статический метод без создания оболочки? Передача функций поддерживается не каждым языком. Статические методы имеют свои варианты использования, и я использую их, когда это имеет смысл . Это, как всегда, случай «только потому, что вы можете, не обязательно означает, что вы должны».
см
13

Вы в значительной степени описали, как именно вы выполняете ООП в C, в котором доступны только статические методы, а «this» является структурным указателем. И да, вы можете сделать полиморфизм времени выполнения в C, указав указатель функции во время построения, который будет сохранен как один элемент структуры. Методы экземпляров, доступные на других языках, являются просто синтаксическим сахаром, который по сути делает то же самое под капотом. Некоторые языки, такие как python, находятся на полпути между ними, где параметр «self» явно указан, но специальный синтаксис для его вызова обрабатывает наследование и полиморфизм для вас.

Карл Билефельдт
источник
FWIW, с C вы можете делать виртуальную диспетчеризацию следующим образом: obj->type->mthd(obj, ...);и это в значительной степени похоже на то, что происходит с виртуальной диспетчеризацией на других языках (но за кулисами).
Donal Fellows
@Karl Можете ли вы предоставить какую-то письменную справку, чтобы начать копать глубже?
Хорхе Лавин
Возможно, вы захотите посмотреть на GObject как на хорошую справочную реализацию.
Карл Билефельдт
10

Проблема со статическим методом возникает в тот момент, когда вам нужен подкласс.

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


источник
2
Это не обязательно проблема. Некоторые языки предоставляют другие механизмы (не виртуальные методы в C ++ и C #) для преднамеренного выполнения того же самого действия - C # даже предоставляет «запечатанные» методы для дальнейшего расширения этой идеи! Очевидно, вы бы этого не делали просто ради этого, но это хорошая техника, о которой нужно знать ...
Shog9
@Steve, это не замена, так как вы все равно можете вызывать оба метода напрямую.
@Steve, в Java статические методы не могут быть переопределены.
@ Thorbjørn - во-первых, вопрос не помечен как Java или какой-либо другой конкретный язык ООП. Что еще более важно, я не говорил, что вы можете переопределить статическую функцию-член. Я пытался использовать аналогию, чтобы сделать точку. Как мне явно не удалось, комментарии удалили.
Steve314
2
В некотором смысле это все еще «новые реализации методов». Просто не в стандартном смысле ООП. Это похоже на то, что я говорю: «Ваша формулировка тоже не идеальна, нур-нур-на-нур-нур» ;-)
Steve314
7

Если вы объявляете метод static, вам не нужно создавать экземпляр объекта из класса (используя newключевое слово) для выполнения метода. Однако вы не можете ссылаться на какие-либо переменные-члены, если они не являются также статическими, и в этом случае эти переменные-члены принадлежат классу, а не конкретному объекту класса.

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

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

В общем случае staticклассы и методы следует использовать только при создании служебных методов, которые принимают один или несколько параметров и возвращают какой-либо объект без каких-либо побочных эффектов (т. Е. Изменения переменных состояния в классе).

Роберт Харви
источник
1
Я думаю, что ОП понимает, что staticзначит, он спрашивает, почему бы просто не объявить все как статичное.
Эд С.
1
Он будет проходит в экземпляре Subject- но в качестве явного параметра, вместо неявного thisпараметра.
Steve314
Ну да ... но я не понимаю твою точку зрения, я полагаю.
Эд С.
@Ed - только то, что вы можете делать практически все что угодно с помощью явного параметра, что вы можете делать с помощью неявного thisпараметра. Существуют различия в ожиданиях, но за пределами наследования нет реальной разницы в том, что вы можете сделать. Например, к этим нестатическим членам можно получить доступ - через явный параметр.
Steve314
Я действовал в предположении, что вы не можете получить доступ к закрытым членам аргумента в C # даже через статический метод, объявленный в том же классе (как вы можете в C ++). Не уверен, почему я так думал, но я был не прав.
Эд С.
3

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

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

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

Мэтисон
источник
1
Ваше утверждение «... это возврат к процедурному программированию ...» заставляет меня думать, что вы считаете его таким плохим, я думаю, что в некотором смысле это часть ОО.
NoChance
making ... unit testing much easier.Нет, не будет. Это делает невозможным любой код, который вызывает статические методы для модульного тестирования, поскольку вы не можете изолировать его от статического метода. Вызов статического метода становится жестко закодированной зависимостью от этого класса.
StuperUser
2

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

Поскольку ваш примерный синтаксис предлагает Java или C #, я думаю, что Торбьерн Равн Андерсен прав, когда указывает на проблему с переопределением (с поздним связыванием). При статическом методе не существует «специального» параметра для поздней привязки, на котором можно основываться, поэтому у вас не может быть поздней привязки.

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

Steve314
источник
2

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

Я НИКОГДА не объявляю статический метод, который принимает тип класса в качестве параметра. Это просто делает код более сложным без выгоды.

Лорен Печтель
источник
3
Вы могли бы это сделать , если вы пропускание objectк staticспособу, и возвращая новый object. Функциональные программисты делают это постоянно.
Роберт Харви
вопрос: считаете ли вы Mathстатический класс «сложным без выгоды», или вы бы предпочли, чтобы у всех типов чисел были виртуальные методы, определяющие абсолютное значение, отрицание, сложение, возведение в квадрат, произвольные корни и т. д.? Я так не думаю. Объекты аккуратны, когда вам нужно сохранить скрытое изменяемое состояние, где вам нужно применить некоторые инварианты. объединяя каждый возможный метод, который работает с типом, в сам тип, это то, что приводит к сложному и не поддерживаемому коду. Я согласен с Робертом, вы можете взглянуть на то, как работают функциональные программисты, это может стать настоящим откровением!
Сара
@kai Как я уже сказал, в значительной степени. Класс Math является разумным исключением, хотя присоединение его к числовым типам не будет плохой идеей.
Лорен Печтел
2

Здесь много хороших ответов, и они намного более проницательны и хорошо осведомлены, чем все, что я когда-либо мог придумать в качестве ответа, но я чувствую, что есть кое-что, на что не обращают внимания:

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

(жирный акцент мой)

Мой 2с по этой части вопроса таков:

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

Спектакль немного отстаивает дьявола на этом: мы можем пойти еще дальше и сказать такие вещи, как:

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

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

По правде говоря, ИМХО, вышеупомянутые пункты (и общий подход «давайте использовать статику, потому что они быстрее») являются аргументами / оценками соломенного человека:

  • если код хороший и, более конкретно, для этого аргумента, если экземпляры создаются только при необходимости (и уничтожаются оптимальным образом), то вы не получаете никаких дополнительных затрат от использования методов insance, когда это уместно (потому что если вы создавать только необходимые объекты, они были бы созданы, даже если бы этот метод был объявлен как static, где else = те же самые издержки инстанцирования)

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

Дракон Шиван
источник
2

Это очень интересный вопрос, ответы на который, как правило, очень разные, в зависимости от того, в каком сообществе вы его задаете. Другие ответы кажутся C # или java bias: язык программирования, который (в основном) чисто объектно-ориентирован и имеет своего рода идиоматическое представление о том, что такое объектная ориентация.

В других сообществах, таких как C ++, объектная ориентация интерпретируется с большей либеральностью. Некоторые хорошо известные эксперты изучили предмет и, на самом деле, пришли к выводу, что свободные функции улучшают инкапсуляцию (акцент мой):

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

Скотт Мейерс

Для тех, кто не знаком с терминологией C ++:

  • функция-член = метод
  • не-членская функция = статический метод
  • non-friend = использовать только публичный API
  • non-member non-friend функция = статический метод, который использует только публичный API

Теперь, чтобы ответить на ваш вопрос:

Разве я не могу использовать все статические методы?

Нет, вы не всегда должны использовать статические методы.

Но вы должны использовать их всякий раз, когда вам нужно использовать только открытый API класса.

authchir
источник
-3

на Яве ...

Статические методы не перезаписываются, а скрываются, поэтому в случае расширения класса будет большая разница (проблема?).

С другой стороны, я заметил, что Java-программисты склонны думать, что все ДОЛЖНО быть объектом, не понимая, почему, и я не согласен.

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

Некоторые хорошие примеры использования статических методов - это независимые функции без побочных эффектов.

также см. ключевое слово синхронизировано ...

никола
источник
2
Как скрывается этот статический метод и где возникнет эта разница и проблема при расширении класса? Как синхронизируется с этим? Где встречаются проблемы статики и наследования? Можете ли вы предоставить некоторые хорошие и плохие статические методы в основных библиотеках Java и объяснить, почему каждый из них имеет место?