stringtranslate.com

Программирование на основе прототипов

Программирование на основе прототипов — это стиль объектно-ориентированного программирования , в котором повторное использование поведения (известное как наследование ) осуществляется посредством процесса повторного использования существующих объектов , которые служат прототипами . Эту модель также можно назвать прототипным , прототипно-ориентированным, бесклассовым или экземплярно-ориентированным программированием.

Программирование на основе прототипов использует обобщенные объекты процесса, которые затем можно клонировать и расширять. На примере фруктов объект «фрукт» будет представлять свойства и функциональность фруктов в целом. Объект «банан» будет клонирован из объекта «фрукт» и к нему будут добавлены общие свойства, характерные для бананов. Каждый отдельный объект «банан» будет клонирован из общего объекта «банан». Сравните с парадигмой , основанной на классах , где класс «фрукт» будет расширен классом «банан » .

Первыми языками программирования , основанными на прототипах , были Director aka Ani (на базе MacLisp ) (1976–1979), а одновременно, а не независимо, ThingLab (на основе Smalltalk ) (1977–1981), соответствующие докторские проекты Кеннета Майкла Кана в Массачусетский технологический институт и Алан Гамильтон Родились в Стэнфорде (но работали с Аланом Кеем в Xerox PARC). Борнинг ввел слово «прототип» в своей статье TOPLAS 1981 года. Первым языком программирования на основе прототипов с более чем одним разработчиком или пользователем, вероятно, был Yale T Scheme (1981–1984), хотя, как и в случае с Director и ThingLab, он изначально говорил только об объектах без классов. Языком, который сделал популярным название и понятие прототипов, был Self (1985–1995), разработанный Дэвидом Ангаром и Рэндаллом Смитом для исследования тем в области объектно-ориентированного языкового проектирования.

С конца 1990-х годов бесклассовая парадигма становится все более популярной. Некоторыми современными языками, ориентированными на прототипы, являются JavaScript (и другие реализации ECMAScript , такие как JScript и Flash ActionScript 1.0 ), Lua , Cecil , NewtonScript , Io , Ioke, MOO , REBOL и AHK .

С 2010-х годов появилось новое поколение языков с чисто функциональными прототипами, которые сводят ООП к самой сути: Jsonnet — динамический ленивый чисто функциональный язык со встроенной объектной системой прототипов, использующей наследование миксинов; Nix — это динамический ленивый чисто функциональный язык, который создает эквивалентную объектную систему («расширения» Nix) буквально в двух коротких определениях функций (плюс множество других удобных функций). Оба языка используются для определения крупных распределенных конфигураций программного обеспечения (Jsonnet напрямую вдохновлен GCL, языком конфигурации Google, с помощью которого Google определяет все свои развертывания, и имеет схожую семантику, хотя и с динамической привязкой переменных). С тех пор другие языки, такие как Gerbil Scheme, реализовали чисто функциональные системы ленивых прототипов, основанные на аналогичных принципах.

Проектирование и реализация

Этимологически «прототип» означает «первая отливка» («отливка» в смысле изготовления). Прототип — это конкретная вещь, из которой путем копирования и модификации могут быть созданы другие объекты. Например, Международный Прототип Килограмма — это реальный реально существующий объект, из которого путем копирования можно создавать новые объекты-килограммы. Для сравнения, «класс» — это абстрактная вещь, которой могут принадлежать объекты. Например, все объекты-килограммы относятся к классу KilogramObject, который может быть подклассом MetricObject, и так далее.

Прототипическое наследование в JavaScript описано Дугласом Крокфордом как

Вы создаете объекты-прототипы, а затем… создаете новые экземпляры. Объекты в JavaScript изменяемы, поэтому мы можем дополнять новые экземпляры, добавляя им новые поля и методы. Затем они могут послужить прототипами для еще более новых объектов. Нам не нужны классы, чтобы создавать множество похожих объектов… Объекты наследуются от объектов. Что может быть более объектно-ориентированным, чем это? [1]

Сторонники программирования на основе прототипов утверждают, что оно побуждает программиста сосредоточиться на поведении некоторого набора примеров и только позже беспокоиться о классификации этих объектов в архетипические объекты, которые позже используются аналогично классам . [2] Многие системы, основанные на прототипах, поощряют изменение прототипов во время выполнения , тогда как только очень немногие объектно-ориентированные системы, основанные на классах (например, динамическая объектно-ориентированная система, Common Lisp , Dylan , Objective-C , Perl , Python , Ruby или Smalltalk ) позволяют изменять классы во время выполнения программы.

Почти все системы, основанные на прототипах, основаны на интерпретируемых и динамически типизированных языках. Однако системы, основанные на статически типизированных языках, технически осуществимы. Язык Omega, обсуждаемый в разделе «Программирование на основе прототипов» [3] , является примером такой системы, хотя, согласно веб-сайту Omega, даже Omega не является исключительно статическим, а скорее его «компилятор может выбрать использование статического связывания там, где это возможно, и может улучшить эффективность программы».

Строительство объекта

В языках, основанных на прототипах, нет явных классов. Объекты наследуются напрямую от других объектов через свойство прототипа. Свойство прототипа вызывается prototypeв Self и JavaScript или protoв Io . Существует два метода создания новых объектов: создание объекта ex nihilo («из ничего») или путем клонирования существующего объекта. Первый поддерживается посредством некоторой формы объектного литерала , объявлений, в которых объекты могут быть определены во время выполнения с помощью специального синтаксиса, например, {...}и переданы непосредственно в переменную. Хотя большинство систем поддерживают различные варианты клонирования, создание объектов ex nihilo не так распространено. [4]

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

Системы, поддерживающие создание объектов ex nihilo, позволяют создавать новые объекты с нуля без клонирования существующего прототипа. Такие системы предоставляют специальный синтаксис для указания свойств и поведения новых объектов без ссылки на существующие объекты. Во многих языках прототипов существует корневой объект, часто называемый Object , который устанавливается в качестве прототипа по умолчанию для всех других объектов, созданных во время выполнения, и который содержит часто необходимые методы, такие как функция, toString()возвращающая описание объекта в виде строки. . Одним из полезных аспектов создания объекта ex nihilo является обеспечение того, чтобы имена слотов (свойств и методов) нового объекта не конфликтовали в пространстве имен с объектом Object верхнего уровня . (В языке JavaScript это можно сделать, используя нулевой прототип, т.е. Object.create(null))

Клонирование относится к процессу, при котором новый объект создается путем копирования поведения существующего объекта (его прототипа). Тогда новый объект несет в себе все качества оригинала. С этого момента новый объект можно изменить. В некоторых системах результирующий дочерний объект сохраняет явную связь (посредством делегирования или сходства ) со своим прототипом, а изменения в прототипе приводят к тому, что соответствующие изменения становятся очевидными в его клоне. Другие системы, такие как Forth -подобный язык программирования Kevo, не распространяют изменения из прототипа таким образом, а вместо этого следуют более конкатенативной модели, в которой изменения в клонированных объектах не распространяются автоматически между потомками. [2]

// Пример истинного прототипного стиля наследования в JavaScript.// Создание объекта с использованием буквальной нотации объекта {}. const foo = { name : "foo" , один : 1 , два : 2 };          // Другой объект. const bar = { два : "два" , три : 3 };        // Object.setPrototypeOf() — метод, представленный в ECMAScript 2015. // Для простоты давайте представим, что следующая // строка работает независимо от используемого движка: Object . setPrototypeOf ( бар , foo ); // foo теперь является прототипом bar.  // Если с этого момента мы попытаемся получить доступ к свойствам foo из bar, // нам это удастся. бар . один ; // Решается до 1. // Свойства дочернего объекта также доступны. бар . три ; // Решается до 3. // Собственные свойства затеняют свойства прототипа. бар . два ; // Решается как "два". бар . имя ; // Не влияет, разрешается в "foo". фу . имя ; // Решается как "foo".   

Другой пример:

const foo = { один : 1 , два : 2 };        // bar.[[prototype]] = foo const bar = Object . создать ( фу );   бар . три = 3 ;  бар . один ; // 1 бар . два ; // 2 бара . три ; // 3   

Делегация

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

Конкатенация

При конкатенативном прототипировании — подходе, реализуемом языком программирования Kevo — нет видимых указателей или ссылок на исходный прототип, из которого клонируется объект. Объект-прототип (родительский) копируется, а не связывается с ним, и делегирование отсутствует. В результате изменения прототипа не отразятся на клонированных объектах. [5] Кстати, язык программирования Cosmos обеспечивает то же самое за счет использования постоянных структур данных . [6]

Основное концептуальное отличие этой схемы заключается в том, что изменения, внесенные в объект-прототип, не распространяются автоматически на клоны. Это можно рассматривать как преимущество или недостаток. (Однако Kevo предоставляет дополнительные примитивы для публикации изменений в наборах объектов на основе их сходства — так называемого семейного сходства или механизма семейства клонов [5] — а не на основе таксономического происхождения, как это типично для модели делегирования.) Это так. также иногда утверждается, что прототипирование на основе делегирования имеет дополнительный недостаток, заключающийся в том, что изменения в дочернем объекте могут повлиять на последующую работу родительского объекта. Однако эта проблема не свойственна модели, основанной на делегировании, и не существует в языках, основанных на делегировании, таких как JavaScript, которые гарантируют, что изменения дочернего объекта всегда записываются в самом дочернем объекте, а не в родительских объектах (т. е. в дочернем объекте). value затеняет родительское значение, а не меняет родительское значение).

В упрощенных реализациях конкатенативное прототипирование будет иметь более быстрый поиск членов, чем прототипирование на основе делегирования (поскольку нет необходимости следовать цепочке родительских объектов), но, наоборот, будет использовать больше памяти (поскольку копируются все слоты, а не существует один слот, указывающий на родительский объект). Однако более сложные реализации могут избежать этой проблемы, хотя и требуется компромисс между скоростью и памятью. Например, системы с конкатенативным прототипированием могут использовать реализацию копирования при записи , чтобы обеспечить скрытое совместное использование данных — и Kevo действительно следует такому подходу. [7] И наоборот, системы с прототипированием на основе делегирования могут использовать кэширование для ускорения поиска данных.

Критика

Сторонники объектных моделей на основе классов, критикующие системы на основе прототипов, часто испытывают опасения, аналогичные опасениям, которые испытывают сторонники систем статических типов для языков программирования в отношении систем динамических типов (см. datatype ). Обычно такие проблемы связаны с корректностью , безопасностью , предсказуемостью , эффективностью и незнанием программиста.

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

Что касается эффективности, объявление классов упрощает многие оптимизации компилятора , которые позволяют разрабатывать эффективные методы и поиск переменных экземпляра. Что касается языка Self , много времени было потрачено на разработку, компиляцию и интерпретацию методов повышения производительности систем на основе прототипов по сравнению с системами на основе классов.

Распространенная критика языков, основанных на прототипах, заключается в том, что сообщество разработчиков программного обеспечения с ними незнакомо, несмотря на популярность и распространение JavaScript на рынке . Однако знания о системах, основанных на прототипах, увеличиваются с распространением фреймворков JavaScript и комплексным использованием JavaScript по мере развития Всемирной паутины (Web). [8] [ нужна цитация ] ECMAScript 6 представил классы как синтаксический сахар вместо существующего в JavaScript наследования на основе прототипов, предоставляя альтернативный способ создания объектов и управления наследованием. [9]

Языки, поддерживающие программирование на основе прототипов

Смотрите также

Рекомендации

  1. ^ Крокфорд, Дуглас. «Прототипическое наследование в JavaScript» . Проверено 22 июня 2021 г.
  2. ^ аб Тайвалсаари, Антеро (1996). «Раздел 1.1». Классы против прототипов: некоторые философские и исторические наблюдения . стр. 44–50. CiteSeerX 10.1.1.56.4713 . 
  3. ^ Блашек, Гюнтер. «Раздел 2.8». Омега: статически типизированные прототипы . п. 177.
  4. ^ Дони, Чистоф; Маленфан, Жак; Барду, Даниэль. «Раздел 1.2» (PDF) . Классификация языков программирования на основе прототипов . п. 17.
  5. ^ аб Антеро Тайвалсаар (2009). «Упрощение JavaScript с помощью наследования прототипов на основе конкатенации» (PDF) . Технологический университет Тампере. Архивировано из оригинала (PDF) 13 августа 2011 г. Проверено 11 марта 2015 г. Kevo реализовал объектную модель, основанную на чистой конкатенации, в которой новые объекты создавались путем копирования, а пространства имен всех объектов всегда были полностью автономными. … Более того, у Kevo был внутренний механизм семейства клонов , который позволял отслеживать «генеалогию» изменений среди групп объектов, так что изменения отдельных объектов могли при необходимости распространяться на другие объекты.
  6. ^ «Объектно-ориентированное программирование». Гитхаб . Проверено 4 сентября 2023 г.
  7. ^ Тайвалсаари, Антеро (1992). «Kevo, объектно-ориентированный язык программирования на основе прототипов, основанный на конкатенации и операциях модулей». Технический отчет Отчет LACIR 92-02 . Университет Виктории.
  8. ^ «Прототип объектно-ориентированного программирования с использованием JavaScript». Список врозь . 26 апреля 2016 г. Проверено 21 октября 2018 г.
  9. ^ «Классы». Справочник по JavaScript . Сеть разработчиков Mozilla . Проверено 9 февраля 2016 г.
  10. ^ Собственный язык сценариев. http://www.davidbrebner.com/?p=4 содержит несколько основных примеров использования.

дальнейшее чтение