Есть ли причина использовать private
ключевое слово вместо protected
? Я могу понять цель этого, когда есть некоторое свойство кэширования или вспомогательный метод, который не имеет смысла в дочернем классе. я вижу private
во многих важных классах, хотя, где я не вижу цели этого. Вот пример Symfony: Класс HttpException Symfony:
namespace Symfony\Component\HttpKernel\Exception;
class HttpException extends \RuntimeException implements HttpExceptionInterface
{
private $statusCode;
private $headers;
public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = array(), $code = 0)
{
$this->statusCode = $statusCode;
$this->headers = $headers;
parent::__construct($message, $code, $previous);
}
public function getStatusCode()
{
return $this->statusCode;
}
public function getHeaders()
{
return $this->headers;
}
}
Есть ли причина, почему они не protected
? Единственное, о чем я могу думать, — это заставить программистов объявлять их самостоятельно для удобства чтения кода, но я не уверен в этом. Пожалуйста, дайте мне знать, что вы думаете об использовании private
Ключевое слово здесь.
Да, есть абсолютная причина, и эта причина — принцип подстановки Лискова или буква «L» в «SOLID», если вы предпочитаете.
Принцип подстановки Лискова гласит, что если у вас есть тип T
и тип S
который является подтипом T
тогда вы сможете заменить любой объект типа T
с объектом типа S
без изменения правильности вашей программы.
protected
члены являются распространенным методом создания нарушений подстановки Лискова, поскольку они позволяют перезаписывать поведение, встречающееся в супертипе.
Это прекрасно в теории, но что это на самом деле означает?
является Отношения, создаваемые наследованием, представляют собой чрезвычайно прочную связь между типами, гораздо более прочную, чем общий интерфейс или композиция, поскольку подразумевают совместное использование реализации. Классическим примером нарушения подстановки Лискова является проблема Круга-Эллипса, см .: https://en.wikipedia.org/wiki/Circle-ellipse_problem
Такая проблема является еще более существенной в производственном мире, где другие разработчики могут в конечном итоге полагаться на поведение, которое, по их мнению, гарантировано наличием супертипа, но на самом деле вообще не гарантируется из-за нарушений LSP — в худшем из возможных случаев они могут в конечном итоге полагаться на некорректное поведение, так что законное исправление ошибки в суперклассе создает проблемы в другом месте.
Всякий раз, когда вы делаете объект / метод более доступным, чем private
вы эффективно даете гарантии другим разработчикам, что в своем подтипе они могут изменить это конкретное поведение практически любым мыслимым образом, и ваш код будет по-прежнему функционировать правильно, поэтому каждый раз, когда вы принимаете это решение, вы должны спросить себя, насколько вы способны сделать эту гарантию. Если вы не уверены в этом, не делайте этого.
Также важно убедиться, что вы не пропускаете детали реализации вашего класса за его пределы через protected
участники, как вы бы через public
члены, потому что подводные камни могут быть такими же неприятными.
Это одна из причин того, что состав объекта обычно предпочтительнее наследования. Дело не в том, что с наследованием в принципе что-то не так, но отношения между типом и супертипом чрезвычайно сильны, и важно убедиться, что эти отношения используются только тогда, когда это уместно.
Давайте посмотрим на ваш пример, он имеет private
код состояния, например. На данный момент этот код состояния является практически неизменным, он устанавливается один раз в конструкторе и не может быть изменен каким-либо открытым методом в классе. Это хорошо, потому что это исключение, мы не хотим, чтобы это изменилось, мы хотим знать о том, что изначально вызвало это.
Если бы вы были, чтобы сделать эти члены protected
, у вас нет гарантии этого больше. Подтип HttpException
которая представляет одну ошибку в один момент, теоретически может представлять собой совершенно другую ошибку в следующий момент. В этой ситуации класс больше не имеет значения, он не служит своей цели.
Да, есть веская причина, особенно если вы пишете классы, которые будут использовать другие. Скрывая реализацию и ограничивая доступ, класс можно реорганизовать, не опасаясь взлома любых пользователей класса.