prototype

Паттерны проектирования. Прототип.

Наши соц. сети: instagram, fb, tg

Паттерны проектирования JavaScript. Прототип.

Эпиграфом для данной статьи нам послужит кусочек текста из Википедии: ”Шаблон проектирования или паттерн (англ. design pattern) в разработке программного обеспечения — повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста. Обычно шаблон не является законченным образцом, который может быть прямо преобразован в код; это лишь пример решения задачи, который можно использовать в различных ситуациях.”

В JavaScript существует несколько паттернов проектирования. С некоторыми из таких паттернов мы уже знакомили вас в предыдущих статьях(Builder, Decorator). Здесь же мы рассмотрим прототип. Прототип является порождающим паттерном. Что же это такое и для чего?

Все по порядку: для начала нам потребуется немного теории.

Типы паттернов проектирования

Существует три основных типа паттернов проектирования: порождающие паттерны, поведенческие паттерны и структурные паттерны.

Порождающие паттерны

Благодаря таким паттернам нам не придётся создавать экземпляры объектов - паттерны данного типа сделают это за нас. Преимущество такого подхода заключается в том, что он дает нашей программе немного больше гибкости при выборе объектов, которые нам необходимо создать. Прототип, паттерн который мы будем рассматривать сегодня, относится именно к этому типу.

Поведенческие паттерны

К этому типу относятся паттерны, ориентированные на связь между объектами.

Структурные паттерны

Паттерны этого типа сфокусированы на композиции классов и объектов. Вы можете использовать их для составления интерфейсов посредством наследования и определения способов составления нескольких объектов для достижения новых функциональных возможностей. Декоратор относится к этой колонке например.

На этом хватит теории. Перейдём непосредственно к практике и юзабельности паттернов. Рассмотрим все это на примере прототипа.

Итак, что же такое прототип и чем он может быть нам полезен?

Прототип

Основная задача этого паттерна - помочь нам в создании объектов, которые можно использовать в качестве шаблона для любых объектов, созданных конструкторами. Это происходит посредством так называемого наследования прототипов.

Поскольку JavaScript имеет встроенную поддержку наследования прототипов, нам не приходится изучать какие-либо новые концепции вне самого синтаксиса.

К примеру, когда мы создаем с помощью конструктора объекты, содержащие свойство name, другие объекты, созданные с помощью той же функции конструктора, также будут иметь такое свойство:

function Movie(title) {
this.title = title
}

 

const harryPotter = new Movie('Harry Potter')
const rushHour2 = new Movie('Rush Hour 2')
const fastAndFurious = new Movie('Fast And Furious')

 

console.log(harryPotter.constructor.name)
console.log(rushHour2.constructor.name)
console.log(fastAndFurious.constructor.name)

Может показаться, что это типичные классовые объекты, верно? На самом деле, здесь мы не используем классы вообще! Паттерн прототипа просто создает копии существующих функциональных объектов, но мы не определяем новые кассовые объекты.

Самым большим преимуществом использования этого паттерна в JavaScript является повышение производительности по сравнению с объектно-ориентированными классами. Это означает, что когда мы определяем функции внутри объекта, они будут создаваться по ссылке. Другими словами, все дочерние объекты будут указывать на один и тот же метод вместо создания отдельных копий!

Пример паттерна в действии:

const Warrior = function(name) {
this.name = name
this.hp = 100
}

 

Warrior.prototype.bash = function(target) {
target.hp -= 15
}

 

Warrior.prototype.omniSlash = function(target) {
// The target's hp may not be under 50 or this attack will fail on the opponent
if (target.hp < 50) {
return
}
target.hp -= 50
}

 

const sam = new Warrior('Sam')
const lenardo = new Warrior('Lenardo')

 

sam.bash(lenardo)

Предположим мы определяем методы атаки воина, используя Warrior.prototype. = function () {...}. Здесь мы создали несколько воинов с новым ключевым словом. Оба экземпляра устанавливают свойство name в соответствии с аргументом name, который был передан вызывающей стороной.

Когда мы определили методы bash и omniSlash в прототипе, два отдельных экземпляра фактически ссылаются на одни и те же функции bash и omniSlash!

const Warrior = function(name) {
this.name = name
this.hp = 100
}

 

Warrior.prototype.bash = function(target) {
target.hp -= 15
}

 

Warrior.prototype.omniSlash = function(target) {
// The target's hp may not be under 50 or this attack will fail on the opponent
if (target.hp < 50) {
return
}
target.hp -= 50
}

 

const sam = new Warrior('Sam')
const lenardo = new Warrior('Lenardo')

 

console.log(sam.bash === lenardo.bash) // true

 

По сути, JavaScript создал еще одну копию (предположительно) того же метода для каждого экземпляра:

 

const Warrior = function(name) {
this.name = name
this.hp = 100

 

this.bash = function(target) {
target.hp -= 15
}

 

this.omniSlash = function(target) {
// The target's hp may not be under 50 or this attack will fail on the opponent
if (target.hp < 50) {
return
}
target.hp -= 50
}
}

 

const sam = new Warrior('Sam')
const lenardo = new Warrior('Lenardo')

 

console.log(sam.bash === lenardo.bash) // false

А теперь представьте, что, если бы мы не использовали шаблон прототипа, как в примере, насколько сумасшедшим это могло бы выйти? Пришлось бы создавать множество экземпляров. Пришлось бы клонировать методы, загромождающие память, которые по сути делают одни и те же вещи!

Другим вариантом расширения прототипов является синтаксис. Это может выглядеть вот так:

const Warrior = function(name) {
this.name = name
this.hp = 100
}

 

Warrior.prototype = {
bash(target) {
target.hp -= 15
},
omniSlash(target) {
// The target's hp may not be under 50 or this attack will fail on the opponent
if (target.hp < 50) {
return
}
target.hp -= 50
},
}

 

Или то же самое, но по-другому:

 

const Warrior = function(name) {
this.name = name
this.hp = 100
}

 

Warrior.prototype.bash = function(target) {
target.hp -= 15
}

 

Warrior.prototype.omniSlash = function(target) {
// The target's hp may not be under 50 or this attack will fail on the opponent
if (target.hp < 50) {
return
}
target.hp -= 50
}

Дорогой читатель, помни о том, что изобретать велосипед не всегда полезно! Есть короткие тропки проторенные сквозь чащу, которые сэкономят тебе время и энергию. Такими тропками в нашем случае являются шаблоны проектирования.

Надеюсь данная статья была вам полезна. Заходите ещё!)