Что такое «дырявая абстракция»?

89

Что означает термин "дырявая абстракция"? (Объясните, пожалуйста, на примерах. Мне часто бывает трудно разобраться в простой теории.)

Geonne
источник
возможный дубликат интерфейсов Fluent и дырявые абстракции
missingfaktor
14
Возможно, вы захотите прочитать оригинальную статью Джоэла Спольски « Закон дырявых абстракций», которая, насколько мне известно, является источником этого термина.
Дэниел Прайден,
1
Большинство ответов предложенного обманщика касаются плавных интерфейсов.
Дэвид Торнли
@David: Второй по количеству голосов пост отвечает, что означает «дырявая абстракция», и с отличным примером.
missingfaktor 07
4
Когда вы через 4 года ответите на этот вопрос в Google, трудно угадать, какой пост был вторым по количеству голосов.
Джон Рейнольдс

Ответы:

106

Вот пример мясного пространства :

У автомобилей есть абстракции для водителей. В чистом виде есть рулевое колесо, акселератор и тормоз. Эта абстракция скрывает множество деталей о том, что находится под капотом: двигатель, кулачки, ремень ГРМ, свечи зажигания, радиатор и т. Д.

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

Это действительно замечательно ... 16- или 80-летний мужчина может управлять этим сложным механизмом, даже не зная, как он работает внутри!

Но утечки есть. Коробка передач небольшая утечка. В автоматической коробке передач вы можете почувствовать, как автомобиль на мгновение теряет мощность при переключении передач, тогда как в CVT вы чувствуете плавный крутящий момент на всем протяжении.

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

Марк Э. Хаазе
источник
7
Спасибо за пример. Никто другой не мог дать простого объяснения.
Себастьян Паттен,
7
Это отличный ответ, особенно потому, что он демонстрирует точку зрения пользователя, а именно о версии программного обеспечения.
chad
1
Что означает мясное пространство? Объяснение непрофессионала?
brumScouse
1
@brumScouse «meatspace» означает физический, автономный мир. Он используется для контраста с сетевым миром киберпространства. Я отредактирую свой ответ, включив ссылку на определение.
Марк Э. Хаасе
1
Мне нравится, как в этом посте указывается, что «утечки все еще есть». Все дело в их минимизации.
alaboudi
51

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

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

tvanfosson
источник
7
@mehaase Я не понимаю, какое значение имеет ваша абстракция негерметичная по замыслу или из-за пренебрежения. Я расширил ответ примером и дополнительной информацией из упомянутой статьи, чтобы он мог стоять сам по себе. Кроме того, я не думаю, что «дырявая абстракция» обязательно должна быть уничижительной. Для меня это просто описывает ситуацию, когда вам, как разработчику, нужно быть более осторожным при работе с абстракцией. Дизайн может быть хорошим, плохим или безразличным, независимо от «дырявости».
tvanfosson
13

В Википедии есть довольно хорошее определение для этого

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

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

Быстрый пример - замыкания C # / VB.Net и их неспособность фиксировать параметры ref / out. Причина, по которой они не могут быть обнаружены, связана с деталями реализации того, как происходит процесс подъема. Это не означает, что есть лучший способ сделать это.

ДжаредПар
источник
13

Вот пример, знакомый разработчикам .NET: Pageкласс ASP.NET пытается скрыть детали операций HTTP, особенно управление данными формы, чтобы разработчикам не приходилось иметь дело с опубликованными значениями (поскольку он автоматически сопоставляет значения формы с сервером). элементы управления).

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

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

Джефф Стернал
источник
У Webforms было дно в ведре. Хуже всего то, что тонко завуалированные абстракции сводились к работе с Http, как если бы вы работали в перчаточном ящике.
brumScouse
9

Что ж, в каком-то смысле это чисто теоретическая вещь, хотя и не маловажная.

Мы используем абстракции, чтобы упростить понимание. Я могу работать со строковым классом на каком-то языке, чтобы скрыть тот факт, что я имею дело с упорядоченным набором символов, которые являются отдельными элементами. Я имею дело с упорядоченным набором символов, чтобы скрыть тот факт, что я имею дело с числами. Я имею дело с числами, чтобы скрыть тот факт, что я имею дело с единицами и нулями.

Дырявая абстракция - это абстракция, которая не скрывает деталей, которые она должна скрывать. Если вызвать string.Length для 5-символьной строки в Java или .NET, я мог бы получить любой ответ от 5 до 10 из-за деталей реализации, где то, что эти языки называют символами, на самом деле являются точками данных UTF-16, которые могут представлять либо 1, либо .5 персонажа. Абстракция просочилась. Однако отсутствие утечки означает, что для определения длины либо потребуется больше места для хранения (для хранения реальной длины), либо изменится с O (1) на O (n) (чтобы определить, какова реальная длина). Если меня волнует настоящий ответ (а на самом деле это не так), вам нужно работать над знанием того, что происходит на самом деле.

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

Джон Ханна
источник
2
И вы работаете с
единицами и нулями
7

Я продолжу в духе приводить примеры с использованием RPC.

В идеальном мире RPC вызов удаленной процедуры должен выглядеть как вызов локальной процедуры (по крайней мере, так гласит история). Он должен быть полностью прозрачным для программиста, чтобы при вызове SomeObject.someFunction()они не знали, хранятся ли SomeObject(или просто someFunctionв этом отношении) локально и выполняются или хранятся и выполняются удаленно. Теоретически это упрощает программирование.

Реальность иная, потому что существует ОГРОМНАЯ разница между вызовом локальной функции (даже если вы используете самый медленный интерпретируемый язык в мире) и:

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

Только по времени это примерно на три порядка (или больше!) Разницы в величине. Эти три с лишним порядка будут иметь огромное значение в производительности, что сделает вашу абстракцию утечки вызова процедуры довольно очевидной, когда вы в первый раз ошибочно трактуете RPC как вызов реальной функции. Кроме того, реальный вызов функции, исключающий серьезные проблемы в вашем коде, будет иметь очень мало точек отказа, кроме ошибок реализации. Вызов RPC имеет все следующие возможные проблемы, которые будут обозначаться как случаи отказа сверх того, что вы ожидаете от обычного местного вызова:

  • возможно, вы не сможете создать экземпляр своего локального прокси
  • возможно, вы не сможете создать экземпляр своего удаленного прокси
  • прокси-серверы могут не подключиться
  • параметры, которые вы отправляете, могут не повлиять на него или вообще
  • возвращаемое значение, которое отправляет удаленный, может не сделать его неповрежденным или вообще

Итак, теперь ваш вызов RPC, который «подобен вызову локальной функции», имеет целую кучу дополнительных условий сбоя, с которыми вам не нужно бороться при выполнении вызовов локальных функций. Абстракция снова просочилась, даже сложнее.

В конце концов, RPC - плохая абстракция, потому что он просачивается как решето на каждом уровне - в случае успеха и в случае неудачи на обоих.

ТОЛЬКО МОЕ ПРАВИЛЬНОЕ МНЕНИЕ
источник
<pimp> Мне больше нравится подход Erlang к этому, поскольку он не пытается скрыть разницу между вызовом функции и отправкой сообщения процессу до такой степени, что они используют совершенно разный синтаксис. И отправка сообщения удаленного процесса заметно отличается от отправки локального процесса, хотя и использует тот же общий синтаксис. </pimp>
ТОЛЬКО МОЕ правильное МНЕНИЕ
2
Ну, это единственный ответ, который на самом деле дает хороший пример (понимание прочитанного, ребята), так что он получает мой +1.
Mark E. Haase
4

Пример из Django ORM многие-ко-многим :

Обратите внимание в примере использования API, что вам необходимо .save () базовый объект Article a1, прежде чем вы сможете добавить объекты Publication к атрибуту многие-ко-многим. И обратите внимание, что обновление атрибута "многие-ко-многим" немедленно сохраняется в базовой базе данных, тогда как обновление единственного атрибута не отражается в базе данных до тех пор, пока не будет вызвана функция .save ().

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

hash1baby
источник
3

Что такое абстракция?

Абстракция - это способ упрощения мира. Это означает, что вам не нужно беспокоиться о том, что на самом деле происходит под капотом или за занавеской. Значит, что-то доказательство идиота.

Пример абстракции: сложности полета на Боинге 737/747 «абстрагированы».

Самолеты - это очень сложные механизмы. У вас есть реактивные двигатели, кислородные системы, электрические системы, системы шасси и т. Д., Но пилоту не нужно беспокоиться о тонкостях реактивного двигателя… все это «отвлечено». Это означает, что пилоту нужно беспокоиться только о том, как управлять самолетом: налево, чтобы идти налево, и направо, чтобы ехать направо, подтягиваться, чтобы набрать высоту, и нажимать вниз, чтобы снизиться.

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

Дырявые абстракции в примере 737

На самом деле пилоту нужно беспокоиться о МНОГО важных вещах - не все было отвлечено: пилоты должны беспокоиться о скорости ветра, тяге, углах атаки, топливе, высоте, погодных условиях, углах снижения и о том, пилот движется в правильном направлении. Компьютеры могут помочь пилоту в этих задачах, но не все автоматизировано / упрощено.

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

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

Эти вещи не абстрагируются. Многие вещи абстрагируются, но не все. Пилоту нужно беспокоиться только о рулевой колонке и, возможно, еще об одном или двух вещах. Абстракция «дырявая».

Дырявые абстракции в коде

...... это то же самое в вашем коде. Если вы не знаете основных деталей реализации, то чаще всего вы загоните себя в угол.

Вот пример кода:

ORM устраняют множество проблем при работе с запросами к базе данных, но если вы когда-либо делали что-то вроде:

User.all.each do |user|
   puts user.name # let's print each user's name
end

Тогда вы поймете, что это хороший способ убить ваше приложение, если у вас больше пары миллионов пользователей. Не все абстрагируется. Вы должны знать, что звонки User.allс 25 миллионами пользователей увеличивают использование памяти и могут вызвать проблемы. Вам нужно знать некоторые основные детали. Абстракция дырявая.

BKSpurgeon
источник
0

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

Например, рассмотрим этот SQLзапрос:

SELECT id, first_name, last_name, age, subject FROM student_details;

И его альтернатива:

SELECT * FROM student_details;

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

Это тривиальный пример, но в конце концов он возвращается к цитате Джоэла Спольски:

Все нетривиальные абстракции в какой-то степени дырявые.

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

Джонни
источник
-1

Предположим, у нас есть следующий код в библиотеке:

Object[] fetchDeviceColorAndModel(String serialNumberOfDevice)
{
    //fetch Device Color and Device Model from DB.
    //create new Object[] and set 0th field with color and 1st field with model value. 
}

Когда потребитель вызывает API, он получает Object []. Потребитель должен понимать, что первое поле массива объектов имеет значение цвета, а второе поле является значением модели. Здесь абстракция просочилась из библиотеки в потребительский код.

Одно из решений - вернуть объект, который инкапсулирует модель и цвет устройства. Потребитель может вызвать этот объект, чтобы получить модель и значение цвета.

DeviceColorAndModel fetchDeviceColorAndModel(String serialNumberOfTheDevice)
{
    //fetch Device Color and Device Model from DB.
    return new DeviceColorAndModel(color, model);
}
Ниранджан Р
источник
-3

Утечка абстракции - это инкапсуляция состояния. очень простой пример дырявой абстракции:

$currentTime = new DateTime();

$bankAccount1->setLastRefresh($currentTime);
$bankAccount2->setLastRefresh($currentTime);
$currentTime->setTimestamp($aTimestamp);

class BankAccount {
    // ...

    public function setLastRefresh(DateTimeImmutable $lastRefresh)
    {
        $this->lastRefresh = $lastRefresh;
    } }

и правильный путь (не дырявая абстракция):

class BankAccount
{
    // ...

    public function setLastRefresh(DateTime $lastRefresh)
    {
        $this->lastRefresh = clone $lastRefresh;
    }
}

подробное описание здесь .

Алиреза Рахмани Халили
источник