понедельник, 25 мая 2009 г.

Программирование на основе прототипов (prototype): понятие, смысл.

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

В объектно-ориентированном подходе (в классическом понимании) разделяют понятия класса и реализации. Первый является абстракцией, описанием структуры и порядка взаимодействия объекта. Реализация - конкретный экземпляр - живёт по законам, описанным внутри класса и не выходит за их рамки. На деле ООП предлагает заниматься разработкой классов и архитектуры их взаимодействия, то есть поступает предложение отойти в сторону и тратить время на создание условий для работы программы.

Для прототипного подхода понятия "класс" не существует. Работа ведётся с конкретной сущностью. Требуется новый экземпляр - клонируем существующий объект, изменяем его как того требует логика программы (модифицируем атрибуты, добавляем новые). Исходный объект - с которого снимается мерка - может быть либо нулевым (пустышкой; требуется наполнение его атрибутами, методами; в дальнейшем он может стать эталоном - прототипом - для следующего клона), либо непустым.

Вот, например, фрагмент кода JavaScript:
function car(plate, model, color) {
this.plate = plate || "missing"
this.model = model || "unknown"
this.color = color || "unknown"
}
function carsInLot(plate, model, color, timeIn, spaceNum) {
this.timeIn = timeIn
this.spaceNum = spaceNum
this.carInfo = car
this.carInfo(plate, model, color)
}
carsInLot.prototype = new car()
var car1 = new carsInLot("123ABC", "Ford","blue","10:02AM", "32")
car.prototype.companyOwned = true

Есть объект car, который является прототипом для расширенного объекта carsInLot. Создаётся переменная car1 - объект carsInLot - с указанием параметров конструктору. Далее у исходного объекта car появлется (на лету) новый атрибут companyOwned. В итоге объект car1 обладает 7-ю атрибутами:
car1.timeIn // value = "10:02AM"
car1.spaceNum // value = "31"
car1.carInfo // value = ссылка на конструктор объекта car - нужно же заполнить поля plate, model, color
car1.plate // value = "AA 123"
car1.model // value = "Ford"
car1.color // value = "blue"
car1.companyOwned // value = true

[caption id="" align="aligncenter" width="439" caption="При появлении нового атрибута у car, он также актуален и для carsInLot"]При появлении нового атрибута у car, он также актуален и для carsInLot[/caption]

[caption id="" align="aligncenter" width="422" caption="Car1 включает 7 атрибутов, причём один из них - объект car"]Car1 включает 7 атрибутов, причём один из них - объект car[/caption]

Клонирование, а в действительности отказ от классов - приводит к переосмыслению цели. Происходит смещение акцентов: процесс создания условий (архитектурные решения, бесчисленные шаблоны проектирования, интерфейсы, абстрагирование) отходит на второй план. Важность приобретает проблема, которую требуется решить. Лёгкий доступ к объекту, способность влиять на состав атрибутов и методов откроет новые горизонты, вот только надо точно знать - для чего? :)

Прототипная (или органическая) модель уже есть и весьма давно. Осталась лишь комплиментарная, которой пока нет.

4 комментария:

ValKon комментирует...

Честно говоря, статья не показывает никаких преимуществ в подобном подходе перед классическим ООП. В реальных проектах при отказе от классов (их эмуляции) и классического наследования становится довольно тяжело поддерживать код.

Сколько не пытался приучить себя писать на javascript в таком стиле, всегда сталкивался с проблемами в построении архитектуры приложения. Поэтому и приходится тянуть из проекта в проект всякие фреймворки типа Mootools.

Mikhail комментирует...

Разве дело в преимуществах? Парадигма программирования предоставляет систему ограничений и возможностей, в рамках которых решается та или иная задача. И в случае прототипного программирования, и в случае ООП решение есть. Просто когда я, например, использую ООП, невольно вспоминаю о возможностях прототипов, но увы использовать их не могу. Например, переопределить функцию в частном случае, не изменяя класс, не при меняя наследования или оберток.

LexaUnforgiven комментирует...

В случае ООП тоже есть возможности, reflections например. Другое дело что вот вам конкретно в этом случае к конкретному экземпляру понадобилось добавить свойство "на лету".. и с точки зрения классов — это странно. можно провести аналогию с безсхемными БД в пику реляционным. Добавляй что угодно, но никто не гарантирует, что то или иное поле у тебя в данный момент присутствует..

Евпаториец комментирует...

"""— это странно. можно провести аналогию с безсхемными БД в пику реляционным. Добавляй что угодно, но никто не гарантирует, что то или иное поле у тебя в данный момент присутствует.."""
это модные ныне NoSQL, в этих двух подходах, на самом деле , много общего , но если у Пети магнитофон, Вася собирает марки, а Маша выходит замуж за Петю, нам просто интересны любые новости. Что, для этого все эти свойства надо описывать в классах? Завтра выяснится что-то другое... Так что добавляй что угодно.