Использование ключевого слова 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(), вы должны переместить этот метод в другой класс и использовать его как зависимость в вызове. класс.

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