Public или private по умолчанию, что выбрать?
На одном из проектов я встретил интересное мнение. Весь код в проекте должен быть максимально открыт по умолчанию. Т.е. мы делаем все методы и поля public
, а final
нигде не ставим. Мотивировано это было сложностями дальнейшей поддержки:
Если я захочу наследовать класс, а там метод
private
, мне придется изучать код, чтобы понять, зачем его сделалиprivate
и могу ли я сделать его публичным.
Это мнение было очень необычным для меня, и я решил выяснить: действительно ли приватные поля и методы принесут больше проблем, чем публичные.
Давайте напишем класс с лишним публичным методом и посмотрим, насколько просто его будет поддерживать.
interface FooInterface {
public function foo();
}
class Foo implements FooInterface {
public function foo() {
// do stuff
$this->bar();
// do stuff
}
public function bar() {
// do stuff
}
}
Условимся, что на момент написания кода метод bar
нигде больше не используется, и технически нам все равно, делать его публичным или приватным.
Private
Если мы сделаем его приватным, то столкнемся с уже озвученными трудностями. Прежде, чем сделать логику этого метода публичной, программист должен будет ознакомиться с кодом метода и выяснить, нет ли в нем мест, специфичных для этого конкретного класса. Например, работы с состоянием.
Public
Если сделать метод публичным по умолчанию, мы избежим вероятности того, что нам понадобится выполнять описанную выше работу в будущем. Шучу, конечно не избежим 😄
Заметка
Никто ведь не гарантирует, что программист напишет метод строго таким образом, чтобы его можно было переиспользовать из любого другого места приложения. Тем более, что изначально метод не планируется быть использованным повторно.
Давайте подумаем, с какими ещё проблемами мы сможем столкнуться через несколько лет после написания такого кода. Мы думаем о будущем, поэтому давайте представим, что мы не меняли этот метод года 3. И теперь нам надо удалить его или изменить сигнатуру. Первый вопрос, который встанет: используется ли этот метод в других местах проекта? Кто угодно из разработчиков мог вызвать публичный метод из любого другого места, и их все надо найти. Вы можете сказать, что современные IDE решают этот вопрос мгновенно, но что, если использовалась черная магия фреймворков?
Заметка
Все эти __get()
и __call()
, вызов метода по произвольному строковому индентификатору, поведения в Yii2, фасады в Laravel и т.п. Наличие таких подходов в проекте делает задачу нетривиальной.
Усложним ее ещё немного: мы нашли несколько мест, где метод bar
использовался, но не вполне понятно, зачем, а авторы того кода уже не работают в вашей компании. Что делать в этом случае?
Добавим в копилку ещё одну потенциальную проблему. Какой-то разработчик решил немного изменить функционал bar
, но не стал глубоко искать вызовы этого метода. А они были, но спрятанные.
Выводы
Публичный метод не решает проблему, озвученную для приватного, но добавляет ворох новых. Потому я однозначно рекомендую все поля, константы и методы классов делать приватными, если они не используются вне класса, в котором объявлены, и не предназначены для этого явно.
Пример рефакторинга из private в public
Итак, в нашем проекте есть версия класса Foo
с приватным методом bar
. В какой-то момент стало очевидно, что функционал этого метода нужен ещё в паре мест проекта. В большинстве случаев можно вынести bar
в отдельный класс, который можно будет использовать где угодно в проекте с помощью любимого DI-контейнера. Итого полный код будет выглядеть так:
class Bar {
public function bar() {
// do stuff
}
}
interface FooInterface {
public function foo();
}
class Foo implements FooInterface {
__construct(private Bar $bar) {}
public function foo() {
// do stuff
$this->bar->bar();
// do stuff
}
}
В этом случае публичный контракт на функционал bar
будет заключён ровно в тот момент, когда он стал нужен. Соответственно, и отношение к нему будет соответствующее: как к сознательно реализованному контракту, а не "еще одному публичному методу, который навряд ли где-нибудь используется". Потому что в таком проекте ничего публичного просто так не делается.