Доброго времени суток, друзья!
В наших прошлом уроке мы c Вами рассмотрели кодовую реализацию очередного свойства ООП - Полиморфизм. Мы наглядно показали ситуацию, при которой возможность использования Полиморфизма в JavaScript’e приходится весьма кстати. На примере наших Родителей (Cat)
и (Dog)
мы увидели, что подход с использованием Полиморфизма может решать вопросы с дублированием кода. Ну а на примере Родителей (Snake)
и (Bird)
мы наглядно продемонстрировали как можно переопределять (расширять) требуемый нам метод, для того чтобы он реализовывал наши текущие требования для конкретных функций-конструкторов.
На данный момент у нас осталось только одно свойство для рассмотрения. Это Инкапсуляция. В одном из наших предыдущих уроков мы рассмотрели это свойство теоретически и даже показали пример работы этого свойства. Правда, сделали мы это без примера кодовой реализации. Сегодня мы углубимся в понятие Инкапсуляции и реализуем его с помощью JavaScript.
Давайте начинать!
УРОК 34. ООП. Часть III. Инкапсуляция.
Если вернуться к нашему теоретическому описанию Инкапсуляции, то мы говорили о возможности закрывать (инкапсулировать) некоторые свойства и методы в наших Родителях от прямого программного доступа.
Дело в том, что в практическом программировании не сказать, что всё, но очень много зависит от способа написания кода. Основная причина такого положения дел обосновывается тем, что ка правило, при разработке серьезных приложений, над программным кодом работает сразу несколько человек (команда). В этой ситуации, любой разработчик может захотеть, чтоб к некоторым методам или свойствам, или переменным не было прямого программного доступа. К примеру, команда взяла на испытательный срок начинающего программиста. Если код будет написан таким образом, что у новичка сразу и ко всему будет доступ, он может просто по неопытности, в порыве абсолютно искреннего желания показать свои лучшие качества, чего-то поломать. Как правило, все последствия такой работы исправляется, и все приходит в норму, но время... Время в разработке играет далеко на самую последнюю роль и тратить на его на такой вид работы не очень хочется.
Безусловно в наших примерах будет сложно воспроизвести такого рода ситуацию. Но показать способы Инкапсуляции данных нам вполне под силу.
Итак, в нашем прошлом уроке мы остановились на том, что сделали общим для всех Родителей метод walk
, а затем используя возможность Полиморфизма, переопределили этот метод для функций-конструкторов (Snake)
и (Bird)
.
На текущий момент наш код должен выглядеть следующим образом:
/* Родитель, Класс, Функция Конструктор */
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('птица');
Давайте обратим внимание на методы, которые присущи нашим Потомкам и вспомним, что кроме метода walk
у нас еще есть методы sleep
и eat
. Давайте в консоли браузера проверим как они работают:
Мы видим, что каждый метод выдает одно и тоже сообщения, однако использует при этом название текущего Потомка. Что ж, это неплохо, но немного скучновато. Давайте добавим нашему методу eat
возможность указывать, какую именно пищу будет есть одно из наших животных. Мы это уже делали, поэтому тут вопросов возникнуть не должно:
/* Родитель, Класс, Функция Конструктор */
var Creature = function() {
}
Creature.prototype = {
sleep: function() {
console.log(this.name + ' спит...');
},
eat: function(food) {
console.log(this.name + ' кушает ' + food);
},
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('птица');
Собственно, все что мы сделали это добавили параметр food
в метод eat
, который будет конкатенироваться (вспоминаем взаимодействие строковых переменных) с основным сообщением. И теперь мы можем «скармливать» нашим питомцам то что пожелаем. Давайте убедимся в этом:
Всё, отлично. Но теперь давайте представим ситуацию, в которой наш вышеупомянутый начинающий разработчик не знает какую именно пищу ест тот или иной питомец. И в таком случае мы рискуем получить нечто вроде этого:
То есть все животные сошли с ума и едят что попало и кого попало. Но нас такая ситуация не страивает и поэтому мы хотим быть точно уверены, что животные будут есть только ту пищу, которая им положена. И кроме того, нам надо это сделать таким образом, чтобы к этому набору «здоровой пищи» не было прямого программного доступа. Вот тут-то нам и поможет Инкапсуляция.
Как вы уже догадались, инкапсулировать (скрывать от прямого программного доступа) мы будем именно пищу. Давайте это сделаем.
/* Родитель, Класс, Функция Конструктор */
var Creature = function() {
}
Creature.prototype = {
sleep: function() {
console.log(this.name + ' спит...');
},
eat: function() {
var food = ['сметану','котлету','молоко','зерно'];
var currentFood;
if(this.name == 'кот') {
currentFood = food[0];
}
if(this.name == 'пёс') {
currentFood = food[1];
}
if(this.name == 'змей') {
currentFood = food[2];
}
if(this.name == 'птица') {
currentFood = food[3];
}
console.log( this.name + ' кушает ' + currentFood);
},
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('птица');
Обратите внимание на то как мы преобразовали метод eat
. Мы создали массив (Array) «здоровой пищи», который назвали food
. Далее мы создали еще одну переменную currentFood
(текущая еда). Этой переменной мы пока не дали никакого значения (однако вспоминая прошлые уроки, мы знаем, что по умолчанию такие переменные имеют значение undefined. А далее, с помощью условного оператора if(…) {…}
и сравнительного оператора ==
(проверяя значение this.name
) мы узнаём какое именно животное использует метод eat
данный момент. И уже зная животное, мы присваиваем нашей переменой currentFood
значение из массива food
используя порядковый номер (Array[n]) нужного нам элемента.
Давайте теперь посмотрим какой именно результат мы получим с таким методом:
По-моему, всё отлично. Несмотря на то, что наш «неопытный разработчик» продолжает скармливать животным то что хочет, наши животные едят только то, что им действительно нравится.
Таким образом, мы Инкапсулировали (спрятали) от прямого программного доступа наш массив с едой food
, переменную текущей еды currentFood
и сам процесс проверки с условными операторами if(…){…}
. И даже если наш «неопытный разработчик» попробует получить доступ к массиву food
, то сделать он этого не сможет.
Ну что ж, мы разобрали последний из трёх основных свойств Объектно- Ориентированного Программирования - Инкапсуляцию. В окончании хотелось бы добавить, что несколько способов реализовать Наследование, Полиморфизм и Инкапсуляцию в JavaScript. Я показал только те, которые с моей точки зрения были наиболее понятны. По мере того как Вы будете углубляться в этот язык программирования Вы так же освоите оставшиеся методы.
Практика.
Ну а в место традиционного «А на сегодня всё» хотел бы сообщить, что в целом наш курс Основы JavaScript для абсолютных новичков! подошел к концу. Благодарю Вас всех за внимание! Всем, кто преодолел все 34 урока - примите моё почтение Вашему упорству!
Безусловно мы разобрали далеко не всё. Но как для новичков я думаю вы получило достаточно информации для того чтобы получить представление о том, как именно работает JavaScript и какие возможности в нём имеются. Единственное, чего вы не получили от этого курса – это прикладной практики. И я думаю, что это не совсем правильно. Одно дело знать какие именно инструменты есть в JavaScript’e и совсем другое - реализовать с помощью этих инструментов нечто рабочее и хоть немного имеющее смысловую нагрузку.
Размышляя в таком русле, я пришел к выводу что нам надо закрепить все приобретённые нами знания в практическом занятии. И для тех, кто еще не иссяк, я предлагаю со следующего урока начать со мной вместе создание Вашего первого приложения на JavaScript. Курс будет называться Практика JavaScript для новичков!.
Конечным продуктом этого курса будет мини игра, чем-то напоминающая известные когда-то игрушки Тамаго́чи. И выглядеть она будет так:
Рабочий прототип можно найти на http://stream.steemul.ru/ant/. Место для нашего проекта нам любозно предоставил делегат голоса @xtar. Спасибо ему большое!
Ссылки на предыдущие уроки:
Урок 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.
Урок 33. ООП. Часть II. Полиморфизм.