ООП для “чайников”. Наследование, инкапсуляция, полиморфизм три кита объектного ориентирования.

Три кита объектного ориентирования это: наследование, инкапсуляция и полиморфизм. Без четкого понимания этих вещей программисту тяжело написать хороший объектно-ориентированный код, использовать всю силу этого подхода, а главное устроится на хорошую работу.
В прошлый раз я рассказывал о классах, объектах, свойствах и методах, если с этими понятиями проблем не возникает, дальше тоже всё будет очень просто. Для наглядности я буду приводить короткие куски кода на PHP (на самом деле язык тут не важен, просто мне кажется это самый распространенный на сегодня синтаксис, хоть и более классическим для примеров ООП является java, мне кажется PHP будет более полезным), поясняющие идею, и давать короткие описания.

 
class Animal 
	function draw 
		return "just animal";
	
 
	function eat 
		return "the animal is eating";
	

 
class Cow extends Animal 
	function draw 
		Return "something that looks like a cow";
	

 

Как видите здесь Корова (Cow) унаследовала функционал от Животного (Animal), изменив реализацию метода draw (конкретизируя как корова на самом деле выглядит), и оставив реализацию метода eat(). Это и есть наследование. Теперь инкапсуляция. Сам по себе этот термин означает «сокрытие». Инкапсуляция, это способ сделать невозможным изменения критичных для работы класса свойств или вызова внутренних методов. Например у нас есть требование: каждое животное должно иметь кличку, и кличка, в течении его жизни не должна меняться. Самое правильное в таком случае это принимать кличку в качестве параметра конструктора (метода выполняемого при создании класса), и хранить его во внутреннем, сокрытом свойстве. Например так:

 
сlass Animal 
 
	private $name;
 
	function __construct$name 
		$this->name = $name;
	
 
	function getName 
		return $this->name;
	
 
	...
 

 

Вот это и есть инкапсуляция. Нет способа изменить кличку снаружи класса, и вы можете быть уверены, что в любом случае, кличка у экземпляра класса будет именно та, что была задана при создании. Ну а теперь полиморфизм. Это тут тоже начнем с примера. Добавим класс Sheep (овца).

 
class Sheep extends Animal 
 
	function draw 
		return 'something that looks like a sheep';
	
 

 

Теперь, предположим что у нас есть класс какого-то животного (любого), и мы всегда можем узнать как оно выглядит, абсолютно независимо от его типа (другими словами с экземпляром какого класса мы имеем дело).

$animal = rand(0,1) ? new Cow('burenka') : new Sheep('kudryashka');
echo $animal->draw();

Данный пример будет случайным образом генерировать экземпляр Коровы и Овцы, и рисовать их.
Нужно сказать что данный пример не совсем «чистый» полиморфизм. Дело в том что полиморфизм подразумевает собой реализацию одного и того же интерфейса в разных классах. Объясню: если бы мы не реализовали метод draw() в одном из классов, у нас переодически возникала бы ошибка обращения к несуществующему методу, а в языках со строгой типизицией, ошибка бы возникала еще на стадии компиляции. Чтобы избежать подобных казусов, нужно использовать итерфейсы (interface):

 
interface IDrawable 
 
	function draw;
 

 
class Cow extends Animal implements IDrawable 
 
	function draw 
		return 'something that looks like a cow';
	
 

 
class Sheep extends Animal implements IDrawable 
 
	function draw 
		return 'something that looks like a sheep';
	
 

 

Как только вы указали, что класс должен реализовывать интерфейс, компилятор или интерпретатор берет на себя обязательство проконтролировать что в классе реализованы методы, описанные в интерфейсе, что позволяет отлавливать ошибки еще до запуска приложения. Вот собственно и всё.