Открытое поле было заброшено в современном ООП для возврата к источникам (хотя метод получения / установки по умолчанию не намного лучше.) Объект инкапсулирует состояние, а поля являются частью его состояния; Этот же объект демонстрирует поведение с помощью открытых методов.
Этот рефакторинг заключается в замене открытого поля полем с более строгой видимостью, к которому затем можно получить доступ или изменить его (при необходимости) с помощью других методов, не обязательно получателя и установщика.
Почему я должен скрывать публичные объекты?
Базовая альтернатива общедоступному полю, геттерам и сеттерам, позволяет вводить косвенное обращение в случае необходимости. Например, вы можете нормализовать значение поля или преобразовать его в вычисляемое поле, поскольку контракт охватывает только метод ($ object-> getName ()), а не фактическое поле ($ this-> name).
Вы также можете опустить метод получения или установки, чтобы получить соответственно закрытое поле, которое полностью инкапсулировано , или конечное поле, которое нельзя изменить после завершения построения объекта. В PHP нет окончательного ключевого слова, хотя оно копирует большую часть объектной модели Java, которая была наиболее распространенной, когда в PHP 5 была представлена новая парадигма.
Вам также может понадобиться предоставлять только открытые методы, а не поля для технических требований . Например, отложенная загрузка Doctrine 2 не может перехватить доступ к общедоступным свойствам для выполнения запроса, поэтому для использования ORM с этим методом необходимо скрыть общедоступные поля.
меры
- Создать геттеры и сеттеры . Просто добытчик, если поле уже оценено в процессе строительства. В противном случае, получатель и установщик позволяют вносить изменения.
- Замените присваивания и показания поля вызовами к получателю или установщику. В случае, если есть назначения, но вы не намереваетесь предоставлять метод установки, лучше вставить метод @deprecated setField () и выполнить его рефакторинг позже, как только все обращения к полю будут проходить оттуда.
- Убедитесь, что тесты все еще зеленые — они должны работать как есть после изменений, за исключением тех же изменений в использовании и назначении. В ходе этого рефакторинга не было внесено больших изменений в дизайн.
- Объявите поле как частное или защищенное. Если у вас уже нет подклассов или вы намерены разрешить использование подклассов в качестве документированной точки расширения, я бы выбрал частную видимость.
В мире PHP существовала распространенная тенденция всегда предоставлять защищенные поля, но не все классы должны (или использоваться) использоваться для создания подклассов, а для некоторых объектов даже нет выбора замены оригиналов в графе объектов каркаса. Беглый взгляд на Symfony 2 показывает, что приватность больше не является табу, в то время как большая часть Zend Framework 2 состоит из портированного кода 1.x и по-прежнему содержит много защищенных полей (это может быть дизайнерским решением).
пример
В исходном состоянии у нас есть класс Reservation, содержащий открытое поле. Это поле может быть изменено в любое время, когда в области находится ссылка на объект Reservation.
<?php class EncapsulateField extends PHPUnit_Framework_TestCase { public function testTheFieldCanBeManipulated() { $reservation = new Reservation(); $reservation->date = '2010-01-01'; $this->assertTrue($reservation->isOutdated()); } } class Reservation { public $date; public function isOutdated() { // global state! Avoid this in real code return $this->date < date('Y-m-d'); } }
Мы добавляем только сеттер; нам не нужен также геттер, чтобы заставить клиентский код работать.
class Reservation { public $date; /** * @param string $date */ public function setDate($date) { $this->date = $date; } public function isOutdated() { // global state! Avoid this in real code return $this->date < date('Y-m-d'); } }
Теперь мы изменим код клиента, представленный только нашим модульным тестом в этом небольшом примере.
<?php class EncapsulateField extends PHPUnit_Framework_TestCase { public function testTheFieldCanBeManipulated() { $reservation = new Reservation(); $reservation->setDate('2010-01-01'); $this->assertTrue($reservation->isOutdated()); } }
Теперь мы можем безопасно ограничить видимость $ this-> date частным и проверить, что нет прямых ссылок на поле снаружи, что приведет к сбою тестов, выполняющих их. PHP не проверяет доступ к элементам на этапе компиляции «на лету»: вам нужны тесты, выполняющие весь ваш код, чтобы убедиться, что вы не обращаетесь к переменной, которая стала закрытой.
class Reservation { private $date; /** * @param string $date */ public function setDate($date) { $this->date = $date; } public function isOutdated() { // global state! Avoid this in real code return $this->date < date('Y-m-d'); } }