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