Прототипное наследование === Flyweight (Приспособленец)

от автора

Хочется поговорить о, возможно, избитой теме наследования в JS и посмотреть на нее немного сдругой стороны. Сразу скажу, предполагается, что язык для вас не в новинку.
К написанию этой статьи меня побудили подобные строчки:

var f = function(){}; f.prototype.prop1 = 'то'; f.prototype.prop2 = 'это'; f.prototype.f = function(){}; 

Паттерн Prototype (Прототип)

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

Кто же такой prototype ?

Первым делом думаю будет правильно вставить определение наследования, от него будем отталкиваться дальше размышляя о насущном.
Насле́дование — один из четырёх важнейших механизмов объектно-ориентированного программирования, позволяющий описать новый класс на основе уже существующего, при этом свойства и функциональность родительского класса заимствуются новым классом. Другими словами, класс-наследник реализует спецификацию уже существующего класса.
Учитывая, что в JS классов нет, концепция наследования на JS представляется мне довольно туманно. Единственный способ описания объекта — использование функции-конструктора. Да и то в любой момент мы можем результирующий объект расширить как нам в голову ударит.

Сколько я не читал статьи про протипное наследование и реализоцию ООП в JS, мне как-то не по себе от всех этих реализаций, поэтому я решил откинуть понятие ООП и посмотреть на JS c другой сторны.
С моей точки зрения, концепция прототипирования являеся изящной реализацией паттерна «Приспособленец».
Основная идея паттерна — различие между внутренним и внешним состоянием объекта. Внешнее состояние передается клиентом, использующим приспособленца, в некотором контексте. Внутреннее состояние хранится непосредственно в приспособленце и позволяет разделять их. Под разделением понимается возможность одновременной работы нескольких клиентов с одним и тем же приспособленцем (prototype).

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

Зачастую можно увидеть такие попытки загнать JS в рамки ООП:

var f = function(){}; f.prototype.prop1 = 'то'; f.prototype.prop2 = 'это'; f.prototype.f = function(){}; 

Это дело еще может оборачиваться в замыкания, в попытках реализовать инкапсуляцию. Такой подход кажется мне костлем в попытках подогнать JS под устаявшуюся модель, кроме того, в случае прототипного наследования, здесь могут возникать проблемы перекрытия свойств или работы различных объектов с одними и теми же, вроде как, приватными переменными.

По факту ООП здесь не стало больше, чем в следующем примере, но зато мы получили реализацию «паттерна ради паттерна» и головную боль которую он доставляет.

var factory = function(){     var context = {         prop1: 'prop1',         prop2: 'prop2',         func: function (arg){             return context.prop1;         };         return {             func = context.func             };    } 

Такой подход ограничевает нас в ряде возможностей, но малость уменьшает нашу головную боль. Есть фабрика которая штампует нам объекты, и методы, которые работают в рамках заданного контекста.
Я специально замкнул методы на переменной context, т.к. в такой реализации полностью отпадает возможность утянуть методы созданного объекта в другие объекты (контексты). Почему? Да просто потому, что я не ожидаю, что кому-то захочется «наследоваться» от него или просто переприсвоить метод этого объекта какому-то другому, а если я этого не ожидаю, то и реализация может не обрабатывать некоторые тонкие моменты, котрые будут вести к магическим ошибкам.

Чем же всетаки удобен оператор new и где использовать протипное наследование?

Не хочется писать, то что уже много раз разобрано, поэтому просто кидаю ссылку на статью паттерна.
Скажу только, что это позволяет сэкономить на памяти при создании множества однотипных объектов с идентичной реализацией некоторых методов.
Добавлю, что в сравнении с C# прототипное наследование нечто похожее на методы расширения. Так сказать, немного разные подходу к одной идее.

Заключение.

Т.к. мне ближе С#, то таким же как я скажу, что концепция прототипирования является более изящным аналагом методов расширения С# или же реализацией паттерна «Приспособленец» (для остальных), которая довольно удачно зарыта в синтаксис языка и зачастую используется разработчиками не по делу.

Ps: Критика принимается

ссылка на оригинал статьи http://habrahabr.ru/post/203022/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *