Работа с наследованием в PHP
Перед изучением данной статьи вы можете прочитать предыдущую статью из этой серии - "Наследование в PHP".
Первый шаг в построении дерева наследования - найти элементы базового класса, которые не соответствуют друг другу или которыми нужно оперировать иначе.
Мы знаем, что методы getPlayLength() и getNumberOfPages() противоречат друг другу. Нам также известно, что нужно создавать разные реализации метода getSummaryLine().
Давайте используем эти различия как основу для создания двух производных классов.
class ShopProduct {
public $numPages;
public $playLength;
public $title;
public $producerMainName;
public $producerFirstName;
public $price;
function __construct( $title, $firstName,
$mainName, $price,
$numPages=0, $playLength=0 ) {
$this->title = $title;
$this->producerFirstName = $firstName;
$this->producerMainName = $mainName;
$this->price = $price;
$this->numPages = $numPages;
$this->playLength = $playLength;
}
function getProducer() {
return "{$this->producerFirstName}".
" {$this->producerMainName}";
}
function getSummaryLine() {
$base ="$this->title ( $this->producerMainName, ";
$base .= "$this->producerFirstName )";
return $base;
}
}
class CdProduct extends ShopProduct {
function getPlayLength() {
return $this->playLength;
}
function getSummaryLine() {
$base ="$this->title ( $this->producerMainName, ";
$base .= "$this->producerFirstName )";
$base .= ": Время звучания - $this->playLength";
return $base;
}
}
class BookProduct extends ShopProduct {
function getNumberOfPages() {
return $this->numPages;
}
function getSummaryLine() {
$base ="$this->title ( $this->producerMainName, ";
$base .= "$this->producerFirstName )";
$base .= ": страниц - $this->numPages";
return $base;
}
}
Чтобы создать дочерний класс, необходимо использовать в объявлении класса ключевое слово extends. В данном примере мы создали два новых класса - BookProduct и СdProduct. Оба они расширяют класс ShopProduct.
Поскольку в производных классах конструкторы не определяются, при создании экземпляров объектов этих классов будет автоматически вызываться конструктор родительского класса.
Дочерние классы наследуют доступ ко всем методам типа public и protected родительского класса (но не к методами и свойствам типа private).
Это означает, что мы можем вызвать метод getProducer() для экземпляра объекта класса CdProduct, хотя метод определен в классе ShopProduct.
$product2 = new CdProduct( "Пропавший без вести",
"Группа", "ДДТ", 10.99, null, 60.33 );
print "Исполнитель: ".$product2->getProducer()."\n";
Таким образом, оба наших дочерних класса наследуют поведение общего родительского класса. И мы можем обращаться с объектом BookProduct так, как будто это объект типа ShopProduct.
Мы можем передать объект BookProduct или CdProduct методу write() класса ShopProductWriter, и все будет работать как надо.
— Регулярная проверка качества ссылок по более чем 100 показателям и ежедневный пересчет показателей качества проекта.
— Все известные форматы ссылок: арендные ссылки, вечные ссылки, публикации (упоминания, мнения, отзывы, статьи, пресс-релизы).
— SeoHammer покажет, где рост или падение, а также запросы, на которые нужно обратить внимание.
SeoHammer еще предоставляет технологию Буст, она ускоряет продвижение в десятки раз, а первые результаты появляются уже в течение первых 7 дней. Зарегистрироваться и Начать продвижение
Обратите внимание на то, что для обеспечения собственной реализации в обоих классах CdProduct и BookProduct переопределяется метод getSummaryLine(). Производные классы могут расширять и изменять функциональность родительских классов. И в то же время каждый класс наследует свойства родительского класса.
Реализация этого метода в суперклассе может показаться избыточной, поскольку метод переопределяется в обоих дочерних классах. Тем не менее, мы предоставляем базовый набор функциональных возможностей, который можно будет использовать в дочернем классе.
Наличие этого метода в суперклассе также гарантирует для клиентского кода, что во всех объектах типа ShopProduct будет присутствовать метод getSummaryLine().
Позже вы увидите, как можно выполнить это требование в базовом классе, не предоставляя никакой его реализации. Каждый дочерний объект класса ShopProduct унаследует все свойства своего родителя. В собственных реализациях метода getSummaryLine() для обоих классов CdProduct и BookProduct обеспечивается доступ к свойству $title.
C понятием наследования сразу разобраться непросто. Определяя класс, который расширяет другой класс, мы гарантируем, что экземпляр его объекта определяется сначала характеристиками дочернего, а затем - родительского класса.
Чтобы понять это, нужно размышлять с точки зрения поиска. При вызове $product2->getProducer() интерпретатор PHP не может найти такой метод в классе CdProduct. Поиск заканчивается неудачей, и поэтому используется стандартная реализация этого метода, заданная в классе ShopProduct.
С другой стороны, когда мы вызываем $product2->getSummaryLine(), то интерпретатор PHP находит реализацию метода getSummaryLine() в классе CdProduct и вызывает его.
То же самое верно и в отношении доступа к свойствам. При обращении к свойству $title в методе getSummaryLine() из класса BookProduct, интерпертатор PHP не находит определение этого свойства в классе BookProduct. Поэтому он использует определение данного свойства, заданное в родительском классе ShopProduct.
Поскольку свойство $title используется в обоих подклассах, оно должно определяться в суперклассе.
Даже поверхностного взгляда на конструктор ShopProduct достаточно, чтобы понять, что в базовом классе по-прежнему выполняется доступ к тем данным, которыми должен оперировать дочерний класс.
Так, конструктору класса BookProduct должен передаваться аргумент $numPages, значение которого заносится в одноименное свойство, а конструктор класса CdProduct должен обрабатывать аргумент и свойство $playLength. Чтобы добиться этого, мы определим методы конструктора в каждом дочернем классе.
— Разгрузит мастера, специалиста или компанию;
— Позволит гибко управлять расписанием и загрузкой;
— Разошлет оповещения о новых услугах или акциях;
— Позволит принять оплату на карту/кошелек/счет;
— Позволит записываться на групповые и персональные посещения;
— Поможет получить от клиента отзывы о визите к вам;
— Включает в себя сервис чаевых.
Для новых пользователей первый месяц бесплатно. Зарегистрироваться в сервисе
Конструкторы и наследование
При определении конструктора в дочернем классе вы берете на себя ответственность за передачу требуемых аргументов родительскому классу. Если же вы этого не сделаете, то у вас получится частично сконструированный объект.
Чтобы вызвать нужный метод из родительского класса, вам понадобится обратиться к самому этому классу через его дескриптор. Для этого случая в PHP предусмотрено ключевое слово parent.
Чтобы обратиться к методу в контексте класса, а не объекта, следует использовать символы "::", а не "->". Поэтому конструкция parent::__construct() означает следующее: "Вызвать метод __construct() родительского класса."
Давайте изменим наш пример так, чтобы каждый класс оперировал только теми данными, которые имеют к нему отношение.
class ShopProduct {
public $title;
public $producerMainName;
private $producerFirstName;
public $price;
function __construct( $title, $firstName,
$mainName, $price ) {
$this->title = $title;
$this->producerFirstName = $firstName;
$this->producerMainName = $mainName;
$this->price = $price;
}
function getProducer() {
return "{$this->producerFirstName}".
" {$this->producerMainName}";
}
function getSummaryLine() {
$base ="$this->title ( $this->producerMainName, ";
$base .= "$this->producerFirstName )";
return $base;
}
}
class CdProduct extends ShopProduct {
public $playLength;
function __construct( $title, $firstName,
$mainName, $price, $playLength ) {
parent::__construct( $title, $firstName,
$mainName, $price );
$this->playLength = $playLength;
}
function getPlayLength() {
return $this->playLength;
}
function getSummaryLine() {
$base ="$this->title ( $this->producerMainName, ";
$base .= "$this->producerFirstName )";
$base .= ": Время звучания - $this->playLength";
return $base;
}
}
class BookProduct extends ShopProduct {
public $numPages;
function __construct( $title, $firstName,
$mainName, $price, $numPages ) {
parent::__construct( $title, $firstName,
$mainName, $price );
$this->numPages = $numPages;
}
function getNumberOfPages() {
return $this->numPages;
}
function getSummaryLine() {
$base ="$this->title ( $this->producerMainName, ";
$base .= "$this->producerFirstName )";
$base .= ": страниц - $this->numPages";
return $base;
}
}
Каждый дочерний класс вызывает конструктор своего родительского класса, прежде чем определять собственные свойства. Базовый класс теперь "знает" только о собственных данных. Дочерние классы - это обычно "специализации" родительских классов.
Следует избегать того, чтобы давать родителским классам какие-либо особые "знания" о дочерних классах.
Вызов переопределенного метода
Ключевое слово parent можно использовать в любом методе, который переопределяет свой эквивалент в родительском классе. Когда мы переопределяем метод, то, возможно, хотим не удалить функции "родителя", а, скорее, расширить их. Достичь этого можно, вызвав метод родительского класса в контексте текущего объекта.
Если вы снова посмотрите на реализацию метода getSummaryLine(), то увидите, что значительная часть кода в них дублируется. И лучше этим воспользоваться, чем заново воспроизводить функциональность, уже разработанную в классе ShopProduct.
// Класс ShopProduct...
function getSummaryLine() {
$base="{$this->title} ( {$this->producerMainName}, ";
$base = .= "{$this->producerFirstName} )";
return $base;
}
// Класс BookProduct...
function getSummaryLine() {
$base = parent::getSummaryLine();
$base .= "страниц - {$this->numPages}";
return $base;
}
Мы определили основные функции для метода getSummaryLine() в базовом классе ShopProduct. Вместо того, чтобы воспроизводить их в подклассах CdProduct и BookProduct, мы просто вызовем родительский метод, прежде чем добавлять дополнительные данные к итоговой строке.
Теперь, когда мы познакомились с основами наследования, можно, наконец, рассмотреть вопрос видимости свойств и методов в свете полной картины происходящего.
Об этом читайте в следующем материале "Управление доступом к классам: спецификаторы доступа public, private и protetced".
Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!
Смотрите также:

















