Значение утечки абстракции?
Что означает термин "Leaky Abstraction"? (Пожалуйста, объясните с примерами. Мне часто трудно ухватиться за простую теорию.)
10 ответов
Вот пример мясного пространства:
В автомобилях есть абстракции для водителей. В чистом виде руль, акселератор и тормоз. Эта абстракция скрывает много деталей о том, что находится под капотом: двигатель, кулачки, ремень ГРМ, свечи зажигания, радиатор и т. Д.
Особенность этой абстракции в том, что мы можем заменить части реализации улучшенными, не переобучая пользователя. Допустим, мы заменили крышку распределителя с электронным зажиганием и заменили фиксированный кулачок на переменный. Эти изменения улучшают рабочие характеристики, но пользователь все еще управляет колесом и использует педали для запуска и остановки.
Это действительно замечательно... 16-летний или 80-летний человек может управлять этим сложным механизмом, не зная, как он работает внутри!
Но есть утечки. Коробка передач - маленькая утечка. В автоматической коробке передач вы можете почувствовать, что машина на мгновение теряет мощность, когда она переключает передачи, в то время как в вариаторах вы чувствуете плавный крутящий момент на всем протяжении.
Есть и большие утечки. Если вы включите двигатель слишком быстро, вы можете повредить его. Если блок двигателя слишком холодный, автомобиль может не запуститься или иметь плохую производительность. И если вы включите радио, фары и переменный ток одновременно, вы увидите, что ваш расход бензина понизится.
Это просто означает, что ваша абстракция раскрывает некоторые детали реализации или что вам необходимо знать о деталях реализации при использовании абстракции. Термин приписывается Джоэлю Спольски, около 2002 года. См. Статью в Википедии для получения дополнительной информации.
Классическим примером являются сетевые библиотеки, которые позволяют обрабатывать удаленные файлы как локальные. Разработчик, использующий эту абстракцию, должен знать, что проблемы с сетью могут привести к сбою, а не к локальным файлам. Затем вам нужно разработать код для обработки ошибок, в частности, за счет абстракции, предоставляемой сетевой библиотекой.
В Википедии есть довольно хорошее определение для этого
Утечка абстракции относится к любой реализованной абстракции, предназначенной для уменьшения (или скрытия) сложности, когда базовые детали не полностью скрыты
Другими словами, для программного обеспечения вы можете наблюдать детали реализации функции через ограничения или побочные эффекты в программе.
В качестве примера можно привести замыкания C# / VB.Net и их неспособность захватывать параметры ref / out. Причина, по которой они не могут быть захвачены, заключается в деталях реализации того, как происходит процесс подъема. Это не значит, что есть лучший способ сделать это.
Вот пример, знакомый разработчикам.NET: ASP.NET Page
Класс пытается скрыть детали HTTP-операций, в частности, управление данными формы, чтобы разработчикам не приходилось иметь дело с опубликованными значениями (поскольку он автоматически отображает значения формы на серверные элементы управления).
Но если вы выходите за рамки самых простых сценариев использования, Page
абстракция начинает протекать, и становится трудно работать со страницами, если вы не понимаете детали реализации класса.
Одним из распространенных примеров является динамическое добавление элементов управления на страницу - значение динамически добавленных элементов управления не будет отображено для вас, если вы не добавите их в нужное время: до того, как базовый механизм отобразит значения входящей формы в соответствующие элементы управления. Когда вы должны изучить это, абстракция просочилась.
Ну, в некотором смысле это чисто теоретическая вещь, хотя и не важная.
Мы используем абстракции, чтобы упростить понимание. Я могу оперировать строковым классом на каком-то языке, чтобы скрыть тот факт, что я имею дело с упорядоченным набором символов, которые являются отдельными элементами. Я имею дело с упорядоченным набором символов, чтобы скрыть тот факт, что я имею дело с числами. Я имею дело с числами, чтобы скрыть тот факт, что я имею дело с 1 и 0.
Утечка абстракции - это та, которая не скрывает детали, которые она должна скрывать. Если вызвать string.Length для 5-символьной строки в Java или.NET, я мог получить любой ответ от 5 до 10, из-за деталей реализации, где то, что эти языки называют символами, на самом деле являются точками данных UTF-16, которые могут представлять либо 1, либо.5 персонажа. Абстракция просочилась. Однако отсутствие утечки означает, что для определения длины потребуется либо больше места для хранения (для хранения реальной длины), либо изменение от O(1) до O(n) (чтобы определить реальную длину). Если я забочусь о реальном ответе (часто вы не очень), вам нужно работать над знанием того, что на самом деле происходит.
Более спорные случаи случаются с такими случаями, как, например, когда метод или свойство позволяет вам проникнуть во внутренние процессы, будь то утечка абстракции или четко определенные способы перехода на более низкий уровень абстракции, иногда может быть вопросом, с которым люди не согласны.
Я продолжу в том же духе приводить примеры с использованием RPC.
В идеальном мире RPC вызов удаленной процедуры должен выглядеть как вызов локальной процедуры (или так будет продолжаться). Он должен быть полностью прозрачным для программиста, чтобы при вызове SomeObject.someFunction()
они понятия не имеют, если SomeObject
(или просто someFunction
в этом отношении) хранятся и исполняются локально или дистанционно. Теория гласит, что это делает программирование проще.
Реальность отличается тем, что существует огромная разница между вызовом локальной функции (даже если вы используете самый медленный интерпретируемый язык в мире) и:
- вызов через прокси-объект
- сериализация ваших параметров
- создание сетевого подключения (если оно еще не установлено)
- передача данных на удаленный прокси
- удаленный прокси восстанавливает данные и вызывает удаленную функцию от вашего имени
- сериализация возвращаемого значения
- передача возвращаемых значений на локальный прокси
- пересборка сериализованных данных
- возвращение ответа от удаленной функции
Только за время разница в три порядка (или больше!). Эти три + порядков будут иметь огромное значение в производительности, что сделает вашу абстракцию утечки вызова процедуры довольно очевидной в первый раз, когда вы ошибочно воспримете RPC как реальный вызов функции. Кроме того, реальный вызов функции, исключающий серьезные проблемы в вашем коде, будет иметь очень мало точек сбоя за пределами ошибок реализации. У вызова RPC есть все следующие возможные проблемы, которые будут рассматриваться как случаи сбоя сверх того, что вы ожидаете от обычного локального вызова:
- вы не сможете создать экземпляр своего локального прокси
- возможно, вы не сможете создать экземпляр удаленного прокси
- прокси не могут подключиться
- параметры, которые вы отправляете, могут не сделать его целым или вовсе
- возвращаемое значение, которое отправляет удаленный, может не сделать его целым или вовсе
Так что теперь ваш вызов RPC, который "точно так же, как вызов локальной функции", имеет целый набор дополнительных условий сбоя, с которыми вам не приходится сталкиваться при выполнении вызовов локальной функции. Абстракция снова просочилась, еще сложнее.
В конце концов, RPC - плохая абстракция, потому что она просачивается как сито на каждом уровне - при успехе и при неудаче обоих.
Что такое абстракция?
Для начала лучше понять, что такое "абстракция"?
Абстракция - это способ упрощения мира. Это означает, что вам не нужно беспокоиться о том, что на самом деле происходит под капотом / за занавеской. Значит, что-то доказательство идиота. Хорошо, что это значит? Лучше всего это проиллюстрировать на примере.
Пример абстракции: сложности полета на Боинге 737/747 "абстрагированы".
Возьмем, к примеру, пассажирский самолет Boeing. Эти самолеты - очень сложные механизмы. У вас есть реактивные двигатели, кислородные системы, электрические системы, системы шасси и т. Д., Но пилоту не нужно беспокоиться о тонкостях реактивного двигателя... все это "абстрагируется", что означает: в конце Днем пилота беспокоили только штурвал и штурвал для управления самолетом. Влево, чтобы идти влево, и вправо, чтобы идти вправо, подтянитесь, чтобы набрать высоту, и нажмите вниз, чтобы спуститься. Это достаточно просто... на самом деле я соврал: управлять рулем немного сложнее. В идеальном мире это единственное, что ему следует беспокоиться о. Но в реальной жизни дело обстоит иначе: если вы управляете самолетом, как обезьяна, без какого-либо реального понимания того, как работает самолет, или каких-либо деталей реализации, то вы, скорее всего, разбитесь и убьете всех на борту.
Дырявые абстракции
На самом деле пилоту нужно беспокоиться о МНОГО важных вещах - не все было отвлечено: пилоты должны беспокоиться о скорости ветра, тяге, углах атаки, топливе, высоте, погодных проблемах, углах снижения, о том, пилот едет в правильном направлении, где сейчас самолет и так далее. Компьютеры могут помочь пилоту в этих задачах, но не все автоматизировано / упрощено.
например, если пилот слишком сильно потянет за колонну - самолет будет подчиняться, но тогда пилот рискует заглохнуть самолет, и если вы остановите самолет, очень трудно восстановить управление, прежде чем он рухнет обратно на землю.
Другими словами, пилоту недостаточно просто управлять штурвалом, ничего не зная......... неееет....... он должен знать о скрытых рисках и ограничениях самолета. прежде чем он улетит....... она должна знать, как самолет работает и как летает; он должен знать детали реализации... она должна знать, что слишком резкое торможение приведет к сваливанию, или что слишком крутая посадка приведет к разрушению самолета.
Эти вещи не абстрагируются. Многие вещи абстрагируются, но не все. Пилоту нужно беспокоиться только о рулевой колонке и, возможно, еще об одном или двух вещах. Абстракция "дырявая".
...... это то же самое в вашем коде. Если вы не знаете основных деталей реализации, то чаще всего вы загоните себя в угол.
Вот пример кода:
ORM устраняют множество проблем при работе с запросами к базе данных, но если вы когда-либо делали что-то вроде:
User.all.each do |user|
puts user.name # let's print each user's name
end
Тогда вы поймете, что это хороший способ убить ваше приложение, если у вас больше пары миллионов пользователей. Не все абстрагируется. Вы должны знать это призваниеUser.all
с 25 миллионами пользователей это увеличит использование вашей памяти и вызовет проблемы. Вам нужно знать некоторые основные детали. Абстракция дырявая.
Пример в примере django ORM "многие ко многим":
В примере использования API обратите внимание, что вам необходимо.save() базовый объект Article a1, прежде чем вы сможете добавить объекты Publication в атрибут многие-ко-многим. И обратите внимание, что обновление атрибута "многие ко многим" немедленно сохраняет в базовой базе данных, тогда как обновление атрибута в единственном числе не отражается в БД до тех пор, пока не будет вызван.save().
Абстракция состоит в том, что мы работаем с графом объектов, где атрибуты с одним значением и атрибуты с несколькими значениями являются просто атрибутами. Но реализация в виде хранилища данных, поддерживаемого реляционной базой данных, протекает... так как система целостности RDBS появляется сквозь тонкую оболочку объектного интерфейса.
Тот факт, что в какой-то момент, который будет зависеть от вашего масштаба и исполнения, вам потребуется ознакомиться с деталями реализации вашей структуры абстракции, чтобы понять, почему она ведет себя так, как она себя ведет.
Например, рассмотрим это SQL
запрос:
SELECT id, first_name, last_name, age, subject FROM student_details;
И это альтернатива:
SELECT * FROM student_details;
Теперь они выглядят как логически эквивалентные решения, но производительность первого лучше благодаря спецификации имен отдельных столбцов.
Это тривиальный пример, но в итоге он возвращается к цитате Джоэла Спольски:
Все нетривиальные абстракции, в какой-то степени, негерметичны.
В какой-то момент, когда вы достигнете определенного масштаба в вашей работе, вы захотите оптимизировать работу вашей БД (SQL). Для этого вам нужно знать, как работают реляционные базы данных. Вначале он был абстрагирован от вас, но он протекает. Вы должны изучить это в какой-то момент.
Утечка абстракции это все о состоянии инкапсуляции. очень простой пример неплотной абстракции:
$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;
}
}
больше описания здесь.
Предположим, у нас есть следующий код в библиотеке:
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);
}