Доброго времени суток, друзья!
В наших прошлом уроке мы c Вами начали разобрали реализацию одного из свойств ООП - Наследования с помощью кода на JavaScript в более подробной форме. Создали двух уровневое Наследование, узнали, как передавать свойства и методы не только от Родителя к потомку, но и от Родителя к Родителю. Познакомились с очень важным свойством prototype и даже успели упомянуть саму логику работы с этим свойством, которая называется прототипной цепочкой.
У нас на очереди следующие свойства ООП парадигмы: Полиморфизм и Инкапсуляция. Начнем мы конечно по порядку, и сначала рассмотрим Полиморфизм.
Давайте начинать!
Но, перед тем как приступать непосредственно к кодовой реализации оставшихся свойств, требуется сказать пару слов об оставшихся свойствах. Зачастую разработчик (программист) безусловно сам решает какой набор инструментов использовать в реализации поставленной перед ним задачи. Это касается как языка, на котором он пишет, так и концепции, в том числе и ООП. Безусловно вы можете построить свое приложения без использования Наследования, Инкапсуляции и Полиморфизма. Так же вы можете использовать только одно из этих свойств: Наследование или Инкапсуляцию. Но у вас никак не выйдет использовать Полиморфизм без Наследования. Последнее свойство всегда идет в паре со свои «старшим братом».
УРОК 33. ООП. Часть II. Полиморфизм.
В прошлом уроке мы уже успели познакомится с этим свойством в теоретическом плане. И я очень надеюсь что примеры из урока про купирование хвоста и кошку Куклачева, были Вам понятны.
В целом, в реализации Полиморфизма в JavaScript нет ничего сложного. Снова-таки просто необходимо понять смысл зачем он нужен. А нужен он нам для переопределения метода или свойства нашего Родителя внутри Потомка.
Вот скажем, в прошлом уроке, когда мы рассматривали Наследование я привел такой пример:
Во-вторых, это будет не совсем правильное описание модели. К примеру, захотим мы добавить змею. А она у нас не ходит, а ползает. Зачем ей тогда спрашивается метод
walk
и т.д.?
Речь шла о том, что в нашем тогдашнем Родителе Animal
присутствовал метод walk
который дословно переводится как ходить
. Но змея - потомок которого мы хотели создать, не умеет ходить. Мы решили этот нюанс тем, что создали для каждого животного своего Родителя. В частности, для нашей змеи это был Snake
. И в каждом таком Родителе указали свой метод передвижения. Если вспомнить нашу схему, вот она:
то методы передвижения я подчеркнул оранжевой линией. Обратите внимание, что метод walk
в Родителях (Cat)
и (Dog)
повторяется. И в данном примере это конечно не критично, но тем не менее это все-таки дублирование одного и того же кода.
На самом же деле этот нюанс можно было решить с помощью Полиморфиза с оговоркой на то, что это целесообразно. Давайте рассмотрим пример в коде. Для этого снова создадим общего Родителя (Creature)
, Родителей: (Cat)
, (Dog)
, (Snake)
и (Bird)
для наших животных и Потомков для каждого Родителя: кота {cat}
, собаку {dog}
, змею {snake}
и птицу {bird}
:
/* Родитель, Класс, Функция Конструктор */
var Creature = function() {
}
Creature.prototype = {
sleep: function() {
console.log(this.name + ' спит...');
},
eat: function() {
console.log(this.name + ' кушает...');
}
}
/* Родитель, Класс, Функция Конструктор (2-го уровня) */
var Cat = function(name) {
this.name = name;
this.tail = 1;
this.paws = 4;
this.walk = function() {
console.log('ходит...');
}
}
Cat.prototype = Creature.prototype;
var Dog = function(name) {
this.name = name;
this.tail = 1;
this.paws = 4;
this.walk = function() {
console.log('ходит...');
}
}
Dog.prototype = Creature.prototype;
var Snake = function(name) {
this.name = name;
this.body = 1;
this.head = 1;
this.crawl = function() {
console.log('ползает...');
}
}
Snake.prototype = Creature.prototype;
var Bird = function(name) {
this.name = name;
this.tail = 1;
this.wings = 4;
this.fly = function() {
console.log('летает...');
}
}
Bird.prototype = Creature.prototype;
/* Потомки */
var cat = new Cat('кот');
var dog = new Dog('пёс');
var snake = new Snake('змей');
var bird = new Bird('птица');
Вот такой код. Оранжевой рамкой выделены наши методы для передвижения. Красные стрелки указывают на дублирование. Давайте избавимся от этого дублирования и перенесем этот метод walk
в родителя первого уровня чтобы он наследовался нашей собакой и котом оттуда.
/* Родитель, Класс, Функция Конструктор */
var Creature = function() {
}
Creature.prototype = {
sleep: function() {
console.log(this.name + ' спит...');
},
eat: function() {
console.log(this.name + ' кушает...');
},
walk: function() {
console.log('ходит...');
}
}
/* Родитель, Класс, Функция Конструктор (2-го уровня) */
var Cat = function(name) {
this.name = name;
this.tail = 1;
this.paws = 4;
}
Cat.prototype = Creature.prototype;
var Dog = function(name) {
this.name = name;
this.tail = 1;
this.paws = 4;
}
Dog.prototype = Creature.prototype;
var Snake = function(name) {
this.name = name;
this.body = 1;
this.head = 1;
this.crawl = function() {
console.log('ползает...');
}
}
Snake.prototype = Creature.prototype;
var Bird = function(name) {
this.name = name;
this.tail = 1;
this.wings = 4;
this.fly = function() {
console.log('летает...');
}
}
Bird.prototype = Creature.prototype;
/* Потомки */
var cat = new Cat('кот');
var dog = new Dog('пёс');
var snake = new Snake('змей');
var bird = new Bird('птица');
Вот мы убрали наше дублирование. Перенесли метод в Родителя (Creature)
и теперь осталось только убедится, что и наш кот, и наша собака всё ещё в состоянии двигаться. Давайте зайдем в консоль и проверим:
Да, все замечательно. Дублирования больше нет, а кот и собака по-прежнему ходят. Но, давайте теперь проверим унаследовали ли этот метод остальные животные:
И мы вынуждены признать, что таки да – унаследовали. И теперь и наша змея, и наша птица умеют ходить. Ну если в случае с птицей это еще более-менее (все-таки лапки у нее есть), то к змее это совсем не приемлемо. Давайте попробуем исправить и эту оплошность.
Смотрите, все эти методы walk
(ходить), crawl
(ползать) и fly
(летать) по большому счету описывают способ передвижения. Значит у каждого нашего животного для описания этих конкретных способов передвижения может использоваться один общий метод, который так и будет называться move
(передвигаться). Давайте переименуем наш walk
в метод move
, чтобы он по-прежнему остался у каждого животного. А в наших Родителях для змеи и птицы мы этот поменяем форм этого метода (то есть используем возможность полиморфизма) для того, чтобы он отражал более правдивую модель поведения этих животных:
/* Родитель, Класс, Функция Конструктор */
var Creature = function() {
}
Creature.prototype = {
sleep: function() {
console.log(this.name + ' спит...');
},
eat: function() {
console.log(this.name + ' кушает...');
},
move: function() {
console.log('ходит...');
}
}
/* Родитель, Класс, Функция Конструктор (2-го уровня) */
var Cat = function(name) {
this.name = name;
this.tail = 1;
this.paws = 4;
}
Cat.prototype = Creature.prototype;
var Dog = function(name) {
this.name = name;
this.tail = 1;
this.paws = 4;
}
Dog.prototype = Creature.prototype;
var Snake = function(name) {
this.name = name;
this.body = 1;
this.head = 1;
this.move = function() {
console.log('ползает...');
}
}
Snake.prototype = Creature.prototype;
var Bird = function(name) {
this.name = name;
this.tail = 1;
this.wings = 4;
this.move = function() {
console.log('летает...');
}
}
Bird.prototype = Creature.prototype;
/* Потомки */
var cat = new Cat('кот');
var dog = new Dog('пёс');
var snake = new Snake('змей');
var bird = new Bird('птица');
И смотрите теперь какая картина у нас получается. Все наши животные обладают методом «двигаться». Собака и Кот наследуют этот метод с самого первого Родителя и умеют ходить, а для Птицы и Змеи мы, используя Полиморфизм переопределили этот метод. И теперь если мы зайдем в консоль и посмотрим результат, то увидим следующее:
Все наши животные могут передвигаться с помощью метода move
, но каждый это будет делать так, как ему требуется. Здесь надо понимать, что также, как и в Наследовании при вызове метода move
начинает работать прототипная цепочка. В случае с Котом и Собакой, JavaScript сначала начинает искать этот метод в сами потомках и не найдя их там обращается к свойству prototype Родителей (Cat)
и (Dog)
. Однако и там их нет, и тогда он идет к этому же свойству, но уже Родителя Creature
. Обнаружив этот метода там – JavaScrit его исполняет.
В случае же со Змеёй и Птицей, все происходит немного быстрее. JavaScript посмотрел в самих ** потомков**, ничего не нашел, пошел к Родителям (Snake)
и (Bird)
, наткнулся на него там и исполнил.
В целом, приблизительно так работает Полиморфизм. В нашем следующем уроке мы рассмотрим оставшееся свойство ООП парадигмы - Инкапсуляцию.
А на сегодня все.
Ссылки на предыдущие уроки:
Урок 1 - Окружение.,
Урок 2 - Некоторые особенности синтаксиса.,
Урок 3 - Переменные.,
Урок 4 - Типы переменных, как основа для их взаимодействия.,
Урок 5 - Операции с переменными одного типа.,
Урок 6 - Операции с переменными одного типа. Часть II.,
Урок 7 - Взаимодействие переменных с разными типами.,
Урок 8 - Взаимодействие переменных разного типа. часть II.,
Урок 9 - Взаимодействие переменных разного типа. Часть III.,
Урок 10 - Другие возможные взаимодействия между переменными.,
Урок 11 - Другие возможные взаимодействия между переменными. Часть II.,
Урок 12 - Другие возможные взаимодействия между переменными. Операторы присваивания.,
Урок 13 - Другие возможные взаимодействия между переменными. Операторы сравнения.,
Урок 14 - Сложные переменные. Array и Object.,
Урок 15 - Условные операторы.),
Урок 16 - Циклы.,
Урок 17 - Циклы. Часть II.,
Урок 18 - Функции.,
Урок 19 - Функции. Часть II.,
Урок 20 - Профилирование. Функции, часть III.,
Урок 21 - Функции, Часть IV. Аргументы.,
Урок 22 - Objects (Объекты).,
Урок 23 - Встроенные функции и объекты.,
Урок 24 - Встроенные функции и Объекты, Часть II. Глобальные функции и переменные.,
Урок 25 - Встроенные функции и Объекты, Часть III. Document Object Model.,
Урок 26 - Встроенные функции и Объекты, Часть III. Document Object Model.
Урок 27 - Встроенные объекты. Объект Style, Events, Часть II.
Урок 28 - Встроенная переменная this. Глобальная и локальная области видимости.
Урок 29 - Объектно-ориентированное Программирование. Введение.
Урок 30. Объектно-ориентированное Программирование. Часть II. Полиморфизм. Инкапусляция.
Урок 31. OОП. Наследование, Часть I. Оператор new.
Урок 32. ООП. Наследование, Часть II. PROTOTYPE.