Использование ключевого слова this не так объектно-ориентировано, как вы могли бы подумать.
Принцип замещения Лискова
Пусть ϕ(x) — доказуемое свойство объектов x типа T. Тогда ϕ(y) должно быть истинным для объектов y типа S, где S — подтип T.
A.K.A.: если он выглядит как утка и крякает как утка, но ему нужны батарейки, вероятно, у вас неправильная абстракция.
Этот принцип в основном говорит о том, что любые ссылки в программе на класс могут быть заменены подтипами этого класса без изменения поведения программы.
Это лежит в основе полиморфизма, величайшей новости, которая пришла к нам с объектно-ориентированным программированием.
Эта замена относительно очевидна, когда у нас есть что-то вроде этого:
Если мы ссылаемся на класс B, B можно заменить на B2.
Но как быть, когда класс ссылается сам на себя? Учти это:
Вызов this.bar() в A всегда будет ссылаться на A#bar. Его нельзя заменить никаким другим объектом. Каждый раз, когда метод #bar вызывается из любого другого метода класса A, он всегда будет указывать на одну и ту же реализацию и будет зависеть исключительно от A для предоставления этой реализации.
Не единственная причина
Нарушение принципа Лискова должно быть достаточной причиной, чтобы сказать нам, что с вызовом this.method() что-то не так, но это не единственная причина, по которой я считаю этот вид вызова необъектно-ориентированным.
Немедленный эффект от вызова this.method() заключается в том, что он делает этот метод статическим!. Учти это:
Есть ли разница в поведении между вызовом метода экземпляра или статического метода? Ответ, конечно же: никакой.
По сути, это означает, что когда метод вызывается внутри класса, он больше не является частью объекта. И мы можем дополнительно продемонстрировать это следующим образом:
Опять никакой разницы в поведении. В этом примере, как и в предыдущих примерах, всякий раз, когда мы вызываем A#foo, вызывается bar(), и у нас нет возможности создать экземпляр объекта A с другой реализацией.
Если вам нужны доказательства на другом уровне, вы можете погуглить модульное тестирование статических методов и посмотреть, как статические методы считаются злом, когда речь идет о тестируемости для тех же аргументов, что и выше.
Итак, мы наследуем и переопределяем!
Кто-то может сказать... и он или она будет иметь в виду это:
И вот оно у нас. Получил 2 разные реализации this.bar(). Верно?
Не совсем.
У нас по-прежнему есть только одна реализация после создания экземпляра объекта. Учти это:
Когда создаются экземпляры объектов A и B, A всегда будет регистрироваться, а B всегда будет предупреждать. И причина этого в том, что мы все еще вызываем this.bar() в обоих случаях, так что на самом деле ничего не изменилось.
Почему это происходит? Или: Третья причина
Вы, наверное, уже заметили, что проблема здесь заключается в нескольких обязанностях. Это означает, что еще одна причина, по которой это не является объектно-ориентированным, заключается в несоблюдении принципа единой ответственности.
Класс A генерирует содержимое («x») и печатает содержимое. Содержание может измениться, и выходной формат может измениться по разным причинам.
Если вы обнаружите, что используете this.method(), у вашего класса, вероятно, есть несколько обязанностей. Если у вас есть пример, чтобы показать обратное, пожалуйста, оставьте комментарий.
Как это исправить?
Мы переносим ответственность за печать контента на другой класс:
Теперь мы создали 2 разные реализации выходного формата, и любой экземпляр A может либо логировать, либо оповещать.
Вывод
Использование this.method() всегда является процедурным программированием и признаком плохого разделения задач.
Каждый раз, когда вы используете this.method(), вы должны переместить этот метод в другой класс и использовать его как зависимость в вызове. класс.
А приватные методы? Они не имеют значения. Это как у человека в офисе, ответственного за отчет, есть личный помощник. Командиру, получающему отчет, совершенно все равно, использует ли этот человек своего помощника или выполняет всю работу сам.