Стать программистом. Часть 33. Основы JavaScript для абсолютных новичков!


Доброго времени суток, друзья!

В наших прошлом уроке мы 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.


Comments 0