374 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			374 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|   | --- | |||
|  | title: Polymorphism with Abstract and Interface | |||
|  | localeTitle: Полиморфизм с абстрактным и интерфейсом | |||
|  | --- | |||
|  | ## Полиморфизм с абстрактным и интерфейсом
 | |||
|  | 
 | |||
|  | _Совместное использование и принудительное исполнение кода с помощью полиморфизма с использованием абстрактного класса и интерфейса_ | |||
|  | 
 | |||
|  | Мы погрузимся глубже в объектно-ориентированное программирование и попытаемся подумать с точки зрения шаблонов проектирования для совместного использования и обеспечения соблюдения нашего кода с помощью полиморфизма. | |||
|  | 
 | |||
|  | ### Абстрактный класс
 | |||
|  | 
 | |||
|  | Предположим, у нас есть класс Man с некоторыми свойствами ( `name` , `age` , `height` , `fav_drinks` и `fav_sports` ) и методы ( `giveFirmHandshakes` , `beStubborn` и `notPutToiletPaper` ). | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |   | |||
|  |  class Man  | |||
|  |  {  | |||
|  |     public $name;  | |||
|  |     public $age;  | |||
|  |     public $height;  | |||
|  |     public $fav_sports;  | |||
|  |     public $fav_drinks;  | |||
|  |   | |||
|  |     public function __construct($name, $age, $height)  | |||
|  |     {  | |||
|  |         $this->name = $name;  | |||
|  |         $this->age = $age;  | |||
|  |         $this->height = $height;  | |||
|  |     }  | |||
|  |   | |||
|  |     public function giveFirmHandshakes()  | |||
|  |     {  | |||
|  |         return "I give firm handshakes.";  | |||
|  |     }  | |||
|  |   | |||
|  |     public function beStubborn()  | |||
|  |     {  | |||
|  |         return "I am stubborn.";  | |||
|  |     }  | |||
|  |   | |||
|  |     public function notPutToiletPaper()  | |||
|  |     {  | |||
|  |         return "It's not humanly possible to remember to put toilet paper rolls when they are finished";  | |||
|  |     }  | |||
|  |  }  | |||
|  | ``` | |||
|  | 
 | |||
|  | Нам нужно указать имя, возраст и высоту, чтобы создать экземпляр этого класса, как требуется конструктору. | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  $jack = new Man('Jack', '26', '5 Feet 6 Inches');  | |||
|  |   | |||
|  |  echo sprintf('%s - %s - %s', $jack->name, $jack->age, $jack->height);  | |||
|  |  // => Jack - 26 - 5 Feet 6 Inches  | |||
|  | ``` | |||
|  | 
 | |||
|  | Теперь предположим, что мы хотим добавить новый метод в этот класс, называемый isActive. | |||
|  | 
 | |||
|  | Этот метод проверяет свойство активным и возвращает соответствующее сообщение в зависимости от значения active со значением по умолчанию false. Мы можем утверждать, что это верно для тех людей, которые активны. | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |   | |||
|  |  class Man  | |||
|  |  {  | |||
|  |     public $name;  | |||
|  |     public $age;  | |||
|  |     public $height;  | |||
|  |     public $fav_sports;  | |||
|  |     public $fav_drinks;  | |||
|  |     public $active = false;  | |||
|  |   | |||
|  |     .....  | |||
|  |     .....  | |||
|  |   | |||
|  |     public function isActive()  | |||
|  |     {  | |||
|  |         if ($this->active == true) {  | |||
|  |             return "I am an active man.";  | |||
|  |         } else {  | |||
|  |             return "I am an idle man.";  | |||
|  |         }  | |||
|  |     }  | |||
|  |  }  | |||
|  |   | |||
|  |  $jack = new Man('Jack', '26', '5 Feet 6 Inches');  | |||
|  |  $jack->active = true;  | |||
|  |  echo $jack->isActive();  | |||
|  |  // => I am an active man.  | |||
|  |   | |||
|  |  $jake = new Man('Jake', '30', '6 Feet');  | |||
|  |  echo "\n" . $jake->isActive();  | |||
|  |  // => I am an idle man.  | |||
|  | ``` | |||
|  | 
 | |||
|  | Что делать, если человек не просто активен или не работает? | |||
|  | 
 | |||
|  | Что, если есть шкала от 1 до 4, которая измеряет, насколько активен человек (1 - холостой, 2 - слегка активный, 3 - умеренно активный, 4 - очень активный)? | |||
|  | 
 | |||
|  | У нас могут быть утверждения if..elseif ... такие как: | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |   | |||
|  |  public function isActive()  | |||
|  |  {  | |||
|  |     if ($this->active == 1) {  | |||
|  |         return "I am an idle man.";  | |||
|  |     } elseif ($this->active == 2) {  | |||
|  |         return "I am a lightly active man.";  | |||
|  |     } elseif ($this->active == 3) {  | |||
|  |         return "I am a moderately active man.";  | |||
|  |     } else {  | |||
|  |         return "I am a very active man.";  | |||
|  |     }  | |||
|  |  }  | |||
|  | ``` | |||
|  | 
 | |||
|  | Теперь давайте сделаем еще один шаг. | |||
|  | 
 | |||
|  | Что, если активное свойство человека - это не просто целое число (1, 2, 3, 4 и т. Д.)? | |||
|  | 
 | |||
|  | Что, если значение активного «атлетическое» или «ленивое»? | |||
|  | 
 | |||
|  | Разве мы не должны добавлять другие инструкции elseif, которые ищут совпадение с этими строками? | |||
|  | 
 | |||
|  | Для такого сценария могут использоваться абстрактные классы. | |||
|  | 
 | |||
|  | С абстрактными классами вы в основном определяете класс как абстрактный и методы, которые вы хотите применять как абстрактные, без фактического ввода кода внутри этих методов. | |||
|  | 
 | |||
|  | Затем вы создаете дочерний класс, расширяющий родительский абстрактный класс и реализующий абстрактные методы в этом дочернем классе. | |||
|  | 
 | |||
|  | Таким образом, вы будете применять все дочерние классы для определения собственной версии абстрактных методов. Давайте посмотрим, как мы можем установить наш `isActive()` как абстрактный. | |||
|  | 
 | |||
|  | # 1: Определите класс как абстрактный.
 | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  abstract class Man  | |||
|  |  {  | |||
|  |  .....  | |||
|  |  .....  | |||
|  |  }  | |||
|  | ``` | |||
|  | 
 | |||
|  | # 2: Создайте абстрактный метод для метода, который вы хотите применить внутри абстрактного класса.
 | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  abstract class Man  | |||
|  |  {  | |||
|  |  .....  | |||
|  |  .....  | |||
|  |  abstract public function isActive();  | |||
|  |  }  | |||
|  | ``` | |||
|  | 
 | |||
|  | # 3: Создайте дочерний класс, расширяющий абстрактный класс.
 | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |   | |||
|  |  class AthleticMan extends Man  | |||
|  |  {  | |||
|  |  .....  | |||
|  |  .....  | |||
|  |  }  | |||
|  | ``` | |||
|  | 
 | |||
|  | # 4: Внедрить абстрактный метод внутри дочернего класса.
 | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  class AthleticMan extends Man  | |||
|  |  {  | |||
|  |     public function isActive()  | |||
|  |     {  | |||
|  |         return "I am a very active athlete.";  | |||
|  |     }  | |||
|  |  }  | |||
|  | ``` | |||
|  | 
 | |||
|  | # 5: Создайте дочерний класс (НЕ абстрактный класс).
 | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  $jack = new AthleticMan('Jack', '26', '5 feet 6 inches');  | |||
|  |  echo $jack->isActive();  | |||
|  |  // => I am a very active athlete.  | |||
|  | ``` | |||
|  | 
 | |||
|  | Полное описание и код реализации абстрактного класса: | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |   | |||
|  |  abstract class Man  | |||
|  |  {  | |||
|  |     public $name;  | |||
|  |     public $age;  | |||
|  |     public $height;  | |||
|  |     public $fav_sports;  | |||
|  |     public $fav_drinks;  | |||
|  |   | |||
|  |     public function __construct($name, $age, $height)  | |||
|  |     {  | |||
|  |         $this->name = $name;  | |||
|  |         $this->age = $age;  | |||
|  |         $this->height = $height;  | |||
|  |     }  | |||
|  |   | |||
|  |     public function giveFirmHandshakes()  | |||
|  |     {  | |||
|  |         return "I give firm handshakes.";  | |||
|  |     }  | |||
|  |   | |||
|  |     public function beStubborn()  | |||
|  |     {  | |||
|  |         return "I am stubborn.";  | |||
|  |     }  | |||
|  |   | |||
|  |     public function notPutToiletPaper()  | |||
|  |     {  | |||
|  |         return "It's not humanly possible to remember to put toilet paper rolls when they are finished";  | |||
|  |     }  | |||
|  |   | |||
|  |     abstract public function isActive();  | |||
|  |  }  | |||
|  |   | |||
|  |  class AthleticMan extends Man  | |||
|  |  {  | |||
|  |     public function isActive()  | |||
|  |     {  | |||
|  |         return "I am a very active athlete.";  | |||
|  |     }  | |||
|  |  }  | |||
|  |   | |||
|  |  $jack = new AthleticMan('Jack', '26', '5 feet 6 inches');  | |||
|  |  echo $jack->isActive();  | |||
|  |  // => I am a very active athlete.  | |||
|  | ``` | |||
|  | 
 | |||
|  | В этом коде вы заметите, что абстрактный абстрактный метод `isActive()` определен внутри абстрактного класса `Man` и реализуется внутри дочернего класса `AthleticMan` . | |||
|  | 
 | |||
|  | Теперь класс `Man` не может быть создан непосредственно для создания объекта. | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  $ted = new Man('Ted', '30', '6 feet');  | |||
|  |  echo $ted->isActive();  | |||
|  |  // => Fatal error:  Uncaught Error: Cannot instantiate abstract class Man  | |||
|  | ``` | |||
|  | 
 | |||
|  | Кроме того, каждый дочерний класс абстрактного класса (класс `Man` ) должен реализовать все абстрактные методы. Отсутствие такой реализации приведет к фатальной ошибке. | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  class LazyMan extends Man  | |||
|  |  {  | |||
|  |   | |||
|  |  }  | |||
|  |   | |||
|  |  $robert = new LazyMan('Robert', '40', '5 feet 10 inches');  | |||
|  |  echo $robert->isActive();  | |||
|  |  // => Fatal error:  Class LazyMan contains 1 abstract method  | |||
|  |  // => and must therefore be declared abstract or implement  | |||
|  |  // => the remaining methods (Man::isActive)  | |||
|  | ``` | |||
|  | 
 | |||
|  | Используя абстрактные классы, вы можете применять определенные методы, которые будут реализованы индивидуально дочерними классами. | |||
|  | 
 | |||
|  | ### Интерфейс
 | |||
|  | 
 | |||
|  | Существует еще одна концепция объектно-ориентированного программирования, которая тесно связана с абстрактными классами, называемыми интерфейсом. | |||
|  | 
 | |||
|  | Единственная разница между абстрактными классами и интерфейсами заключается в том, что в абстрактных классах вы можете иметь сочетание определенных методов ( `giveFirmHandshakes()` , `isStubborn()` и т. Д.) И абстрактных методов ( `isActive()` ) внутри родительского класса, тогда как в интерфейсах, вы можете определять методы (не реализуйте) внутри родительского класса. | |||
|  | 
 | |||
|  | Давайте посмотрим, как мы можем преобразовать абстрактный класс Man выше в интерфейс. | |||
|  | 
 | |||
|  | # 1: Определите интерфейс со всеми методами (используйте интерфейс вместо класса).
 | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  interface Man  | |||
|  |  {  | |||
|  |     public function __construct($name, $age, $height);  | |||
|  |   | |||
|  |     public function giveFirmHandshakes();  | |||
|  |   | |||
|  |     public function beStubborn();  | |||
|  |   | |||
|  |     public function notPutToiletPaper();  | |||
|  |   | |||
|  |     public function isActive();  | |||
|  |  }  | |||
|  | ``` | |||
|  | 
 | |||
|  | # 2: Создайте класс, реализующий интерфейс (используйте инструменты вместо расширений). Этот класс должен реализовать ВСЕ методы, определенные внутри интерфейса, включая метод конструктора.
 | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  class AthleticMan implements Man  | |||
|  |  {  | |||
|  |     public $name;  | |||
|  |     public $age;  | |||
|  |     public $height;  | |||
|  |   | |||
|  |     public function __construct($name, $age, $height)  | |||
|  |     {  | |||
|  |         $this->name = $name;  | |||
|  |         $this->age = $age;  | |||
|  |         $this->height = $height;  | |||
|  |     }  | |||
|  |   | |||
|  |     public function giveFirmHandshakes()  | |||
|  |     {  | |||
|  |         return "I give firm handshakes.";  | |||
|  |     }  | |||
|  |   | |||
|  |     public function beStubborn()  | |||
|  |     {  | |||
|  |         return "I am stubborn.";  | |||
|  |     }  | |||
|  |   | |||
|  |     public function notPutToiletPaper()  | |||
|  |     {  | |||
|  |         return "It's not humanly possible to remember to put toilet paper rolls when they are finished";  | |||
|  |     }  | |||
|  |   | |||
|  |     public function isActive()  | |||
|  |     {  | |||
|  |         return "I am a very active athlete.";  | |||
|  |     }  | |||
|  |  }  | |||
|  | ``` | |||
|  | 
 | |||
|  | # 3: Создайте исполняемый класс (AthleticMan)
 | |||
|  | 
 | |||
|  | ```php | |||
|  | <?php  | |||
|  |  $jack = new AthleticMan('Jack', '26', '5 feet 6 inches');  | |||
|  |  echo $jack->isActive();  | |||
|  |  // => I am a very active athlete.  | |||
|  | ``` | |||
|  | 
 | |||
|  | С интерфейсами вам нужно иметь в виду, что: | |||
|  | 
 | |||
|  | *   Методы не могут быть реализованы внутри интерфейса. | |||
|  |      | |||
|  | *   Переменные (свойства) не могут быть определены внутри интерфейса. | |||
|  |      | |||
|  | *   Все методы, определенные внутри интерфейса, должны быть реализованы в классе child (implementation). | |||
|  |      | |||
|  | *   Все необходимые переменные должны быть определены внутри дочернего класса. | |||
|  |      | |||
|  | *   Интерфейс Man наставляет свои классы реализации для реализации всех методов в интерфейсе. | |||
|  |      | |||
|  | 
 | |||
|  | Итак, что такое использование интерфейсов? | |||
|  | 
 | |||
|  | Разве мы не можем просто создать новый класс AthleticMan и создать все методы вместо реализации интерфейса? | |||
|  | 
 | |||
|  | Именно здесь _разрабатываются шаблоны проектирования_ . | |||
|  | 
 | |||
|  | Интерфейсы используются, когда есть базовый класс ( `Man` ), который хочет заставить вас делать что-либо (создайте объект, giveFirmHandshakes, beStubborn, notPutToiletPaper и проверьте, активны ли вы), но не хочет точно сказать вам, как это сделать , | |||
|  | 
 | |||
|  | Вы можете просто начать реализацию классов с реализацией, как вы сочтете нужным. | |||
|  | 
 | |||
|  | Пока все методы реализованы, интерфейсу `Man` все равно, как это сделать. | |||
|  | 
 | |||
|  | Мы рассмотрели, как и когда использовать абстрактные классы и интерфейсы в PHP. Использование этих концепций ООП для классов с разной функциональностью, использующих один и тот же базовый «план» (абстрактный класс или интерфейс), называется полиморфизмом. |