avva: (Default)
[personal profile] avva
Много думал о двух статьях о программировании, которые недавно прочитал - об объектно-ориентированном программировании и не только.

William R. Cook. On Understanding Data Abstraction, Revisited: https://www.cs.utexas.edu/~wcook/Drafts/2009/essay.pdf

Jonathan Aldrich. The Power of Interoperability: Why Objects Are Inevitable:
https://www.cs.cmu.edu/~aldrich/papers/objects-essay.pdf

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

Я сначала прочитал статью Кука (2009), много с ней спорил в голове, сформулировал какой-то ответ, а потом обнаружил к некоторому замешательству, что по большей части этот ответ содержится в статье Алдрича (2014). Тем не менее хочу попробовать упорядочить свои мысли на эти темы.

Начну с Кука. Он аккуратно определяет и проясняет, что имеет в виду под Абстрактными Типами Данных и объектами, и на первый взгляд это не выглядит особо интересным, потому что в современных языках программирования и то и другое воплощают обычно с помощью объектов. Но если его разграничение действительно улавливает некую существенную разницу между двумя естественными категориями - а я склонен с этим согласиться - то это все равно может оказаться интересным и полезным.

Абстрактный Тип Данных - это когда объявляется новый тип данных, доступ к которым возможен не через операторы языка, типа + или -, а исключительно через набор функций, которые обрабатывают скрытую репрезентацию этих данных. Самый естественный пример, на мой взгляд (не тот, что приводит Кук) - это BigInt. Опытные программисты наверняка встречались с имплементациями целых чисел неограниченной длины в разных языках. Обычно мы используем BigInt через некий API, который дает нам возможность создать такое число с обычным целым значением, делать с ними арифметические операции, печатать, вынимать значение по частям, итд. Функции, которые имлементируют этот API, опираются на какое-то внутреннее представление, например через массив целых чисел, но мы подробности этого представления не знаем и обычно знать не хотим.

В языке типа C такой АТД, как BigInt, скорее всего будет воплощен как структура и набор функций для манипуляции ее содержимым. Но в C++, Java итд. это будет класс, каждый объект которого будет конкретным большим числом: хранить внутри себя репрезентацию и давать возможность вызвать методы Add, Multiply, Create итд.

Обратите внимание на две важных особенности типичного АТД. Во-первых, функции, работающие с ATD, всегда имеют доступ к внутренней репрезентации, в том числе больше, чем у одного значения. Если я вызываю number1.Multiply(number2), то код метода Multiply может смотреть на внутреннюю репрезентацию как "своего" number1, так и "дополнительного" number2, и использовать их для эффективного умножения. Это так несмотря на то, что, казалось бы, number2 - другой объект. Во-вторых, функция Multiply обычно есть только одна, и я незаинтересован в том, чтобы их было много разных, работающих с разными репрезентациями. Даже если по какой-то причине мне надо использовать одновременно два разных пакета BigInt одновременно, я не могу ожидать, что number1.Multiply(number2) будет работать, если number1 и number2 принадлежат к разным АТД.

С другой стороны, что такое "объекты", по Куку? С его точки зрения, главной особенностью объектно-ориентированного программирования следует считать связку интерфейс-объект. Интерфейс - это набор деклараций функций с какой-то общей целью, а объект - это определенное воплощение всех этих функций, упакованное в общую оболочку, через которую это конкретное воплощение функций всегда можно вызвать. Для примера, в языке Go интерфейс Reader состоит из одной функции:

Read(p []byte) (n int, err error)

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

Файлы имплементируют функцию Read одним способом, буферы памяти другим, синхронизированные каналы третьим итд. итп. Переменная типа Reader может хранить объект файла или буфера или канала, и когда через нее вызовут Read(), исполнится правильный код. Для Кука это и есть главное в объектах, а, например, классы или наследование - это второстепенное и необязательное (в Go и нет классов, собственно).

Обратите внимание на полную противоположность ситуации с АТД, где была одна функция Multiply, которая имела доступ к внутренней репрезентации данных любого значения. Тут у нас есть полным-полно разных функций Read, и разные объекты не имеют доступа к "внутренней памяти" друг друга. Скажем, два объекта-файла не более родные друг другу, чем файл и буфер; все равно все, что они могут делать друг с другом, это вызывать Read(). "Внутренняя память" - скажем, дескриптор файла или положение в буфере - вообще неважна для Кука и не является частью объекта (хоть де-факто в популярных языках программирования это не так). Объект состоит из функций, которые на худой конец всегда можно сделать замыканиями, хранящими контекст и пристраивающими его к коду настоящей функции. Наглядным проявлением этого подхода является то, что интерфейс (в языках, где он явно определяется) состоит только из методов, а не из данных - в отличие от класса.

В языках типа C++/Java и АДТ и "объект" по Куку будут объектами, но типичное использование их будет совсем разным. АДТ можно отличить по тому, что их используют через классы, не через интерфейсы. Действительно, если представить на секунду, что BigInt был бы интерфейсом, а SpecificBigInt - конкретным классом-воплощением, то совершенно непонятно, как метод Multiply этого класса будет умножать SpecificBigInt на просто BigInt - у него нет доступа к внутренней репрезентации второго множителя (в некоторых языках техническую сложность воплощения класса можно решить с помощью механизма multiple dispatch, но фундаментальная проблема разных репрезентаций остается).

По Куку, наблюдается красивая дуальность: АТД и объекты реализуют разные способы "абстракции", т.е. как спрятать от себя конкретные детали программистской работы. В случае АТД мы прячем данные (во внутреннюю репрезентацию), но то, какая функция вызывается, остается известным и детерминированным. В случае объектов мы работаем с известными данными (теми, что передаем методам объектов - "внутренняя память" тут не считается, она деталь имплементации), но какая именно функция вызывается - неизвестно, в разных случаях разная. В 1970-х было несколько языков, которые давали прямой доступ к абстракции АТД, но потом взлет популярности объектов пресек эту тенденцию, и вместо этого АТД воплощают сегодня с помощью чего-то типа классов.

Меня эта дуальность не очень впечатляет. Разница между АТД и объектами в смысле Кука действительно есть и очень важна, но для того, чтобы уложить их в симметрично-дуальные ячейки, Кук нивелирует до состояния неважной детали данные, сидящие внутри объекта, а это противоречит тому, как программисты обычно думают об объектах. Скорее я бы сказал, что АТД являются важным частным примером абстракции, а объекты в смысле Кука - т.е. взаимозаменяемые воплощения интерфейсов, опирающиеся на внутренние данные, общие для всех функций данного объекта - являются намного более общим механизмом, краеугольным камнем современных сложных программ. Примерно то же говорит в своей статье Алдрич, и об этом - в следующей записи. Продолжение следует.

Date: 2024-08-07 05:27 pm (UTC)
From: [personal profile] bowhill
В классовой модели вообще много систематического бреда (хорошо, умозрительных спекуляций) которые сначала ярыми апологетами, а потом и остальными стали восприниматься как норма: хотя бы и озвученный пример, когда взаимодействие двух реальных сущностей есть метод некой другой, несуществующей. И довольно бысто всё настолько обрастает вымыслом, что поддержка вымышленных сущностей и их взаимосвязей забирает большие или даже наибольшие усилия.

И это только часть ложносвязности, это если ещё не браться за фундаментальные проблемы классовой модели: за иерархическую схему, за проблемы динамики, за разделение языка и текста, как и прочее.

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

Date: 2024-08-08 07:51 am (UTC)
straktor: benders (Default)
From: [personal profile] straktor
и только вы один умный, стоите весь в белом :)

а ещё расскажите нам про людей
миллионы дебилов-славян дурацки корячатся-говорят своими кривыми недоязыками-огрызками, в то время как уже сотни лет существует один великий и могучий.... так по-вашему, нет?

February 2026

S M T W T F S
1 2 3 4 5 67
891011121314
15161718192021
22232425262728

Most Popular Tags

Page Summary

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 6th, 2026 09:26 pm
Powered by Dreamwidth Studios