Я читаю книгу « Принципы, практики и модели внедрения зависимостей» и читаю концепцию утечки абстракций, которая хорошо описана в книге.
В эти дни я делаю рефакторинг базы кода C #, используя внедрение зависимостей, так что вместо блокирующих используются асинхронные вызовы. Поступая так, я рассматриваю некоторые интерфейсы, которые представляют абстракции в моей базе кода и которые должны быть переработаны, чтобы можно было использовать асинхронные вызовы.
В качестве примера рассмотрим следующий интерфейс, представляющий хранилище для пользователей приложения:
public interface IUserRepository
{
Task<IEnumerable<User>> GetAllAsync();
}
Согласно определению книги, утечка-абстракция - это абстракция, разработанная с учетом конкретной реализации, так что некоторые детали реализации «просачиваются» через саму абстракцию.
Мой вопрос заключается в следующем: можем ли мы рассматривать интерфейс, разработанный с учетом асинхронности, такой как IUserRepository, в качестве примера Leaky Abstraction?
Конечно, не все возможные реализации имеют какое-то отношение к асинхронности: только реализации вне процесса (например, реализация SQL) делают, но репозиторий в памяти не требует асинхронности (фактически реализация версии интерфейса в памяти, вероятно, более трудно, если интерфейс предоставляет асинхронные методы, например, вам, вероятно, придется возвращать что-то вроде Task.CompletedTask или Task.FromResult (users) в реализациях метода).
Что ты об этом думаешь ?
источник
Task
. Принципы суффиксации асинхронных методов к слову асинхронные заключались в том, чтобы различать идентичные вызовы API, в противном случае (отправка C # не может быть основана на типе возвращаемого значения). В нашей компании мы бросили все это вместе.Ответы:
Можно, конечно, ссылаться на закон об вытекающих абстракциях , но это не особенно интересно, поскольку в нем утверждается, что все абстракции являются вытекающими. Можно спорить за и против этой гипотезы, но это не поможет, если мы не разделяем понимание того, что мы подразумеваем под абстракцией , и что мы подразумеваем под утечкой . Поэтому сначала я попытаюсь описать, как я рассматриваю каждое из этих терминов:
Абстракции
Мое любимое определение абстракций взято из APPP Роберта Мартина :
Таким образом, интерфейсы сами по себе не являются абстракциями . Они только абстракции, если они выводят на поверхность то, что имеет значение, и скрывают все остальное.
неплотный
В книге « Принципы, модели и практики внедрения зависимостей» термин « вытекающая абстракция» используется в контексте внедрения зависимостей (DI). Полиморфизм и принципы SOLID играют большую роль в этом контексте.
Из принципа инверсии зависимостей (DIP) следует, снова цитируя APPP, что:
Это означает, что клиенты (вызывающий код) определяют требуемые им абстракции, а затем вы переходите к реализации этой абстракции.
Вытекающей абстракции , на мой взгляд, это абстракция , которая нарушает ДИП по каким - то образом , включая некоторые функции , которые клиент не нужно .
Синхронные зависимости
Клиент, который реализует часть бизнес-логики, обычно использует DI, чтобы отделить себя от определенных деталей реализации, таких как, как правило, базы данных.
Рассмотрим объект домена, который обрабатывает запрос на бронирование ресторана:
Здесь
IReservationsRepository
зависимость определяется исключительно клиентом,MaîtreD
классом:Этот интерфейс является полностью синхронным, поскольку
MaîtreD
классу не нужно, чтобы он был асинхронным.Асинхронные зависимости
Вы можете легко изменить интерфейс на асинхронный:
MaîtreD
Класс, однако, не нужны эти методы , чтобы быть асинхронными, так что теперь DIP нарушается. Я считаю это утечкой абстракции, потому что детали реализации заставляют клиента меняться.TryAccept
Метод теперь также должен стать асинхронной:Нет логического обоснования асинхронности логики домена, но теперь для поддержки асинхронности реализации это необходимо.
Лучшие варианты
На NDC Sydney 2018 я выступил с докладом на эту тему . В нем я также обрисую альтернативу, которая не протекает. Я также буду выступать на нескольких конференциях в 2019 году, но теперь переименован в новый заголовок Async инъекция .
Я планирую также опубликовать серию постов в блоге, чтобы сопровождать доклад. Эти статьи уже написаны и находятся в моей очереди, ожидая публикации, так что следите за обновлениями.
источник
Это не дырявая абстракция вообще.
Быть асинхронным - это фундаментальное изменение в определении функции - это означает, что задача не завершена, когда вызов возвращается, но это также означает, что выполнение вашей программы будет продолжаться почти сразу, не с большой задержкой. Асинхронная и синхронная функции, выполняющие одну и ту же задачу, являются существенно разными функциями. Быть асинхронным не является деталью реализации. Это часть определения функции.
Если функция показывает, как она была сделана асинхронной, это будет утечка. Вы (не / не должны) заботиться о том, как это реализовано.
источник
async
Атрибут метода является тег , который указывает на то, что особое внимание и обработка не требуется. Как таковой, он должен просочиться в мир. Асинхронные операции чрезвычайно сложно правильно составить, поэтому важно дать пользователю API преимущество.Если вместо этого ваша библиотека должным образом управляет всей асинхронной активностью внутри себя, то вы можете позволить себе не
async
«вытекать» из API.В программном обеспечении есть четыре аспекта сложности: данные, управление, пространство и время. Асинхронные операции охватывают все четыре измерения, поэтому требуют наибольшего внимания.
источник
Не совсем. Абстракция - это концептуальная вещь, которая игнорирует некоторые элементы более сложной конкретной вещи или проблемы (чтобы сделать вещь / проблему более простой, доступной или из-за некоторой другой выгоды). Как таковая, она обязательно отличается от фактической вещи / проблемы, и, таким образом, она будет неплотной в некотором подмножестве случаев (т. Е. Все абстракции являются неплотными, единственный вопрос в том, в какой степени - значение, в каких случаях абстракция нам полезно, какова его область применимости).
Тем не менее, когда дело доходит до абстракций программного обеспечения, иногда (или, может быть, достаточно часто?) Детали, которые мы решили игнорировать, на самом деле нельзя игнорировать, поскольку они влияют на некоторые аспекты программного обеспечения, которые важны для нас (производительность, удобство обслуживания, ...) , Таким образом, негерметичная абстракция - это абстракция, предназначенная для игнорирования определенных деталей (при условии, что это было возможно и полезно сделать), но затем оказалось, что некоторые из этих деталей важны на практике (их нельзя игнорировать, поэтому они "протекать").
Таким образом, интерфейс подвергая деталь реализации не вытекающий сам по себе (или , скорее, интерфейс, рассматривается в изоляции, не является самим по себе негерметичных абстракции); вместо этого утечка зависит от кода, который реализует интерфейс (способен ли он фактически поддерживать абстракцию, представленную интерфейсом), а также от предположений, сделанных клиентским кодом (что составляет концептуальную абстракцию, которая дополняет ту, что выражается интерфейс, но сам по себе не может быть выражен в коде (например, возможности языка недостаточно выразительны, поэтому мы можем описать его в документах и т. д.)).
источник
Рассмотрим следующие примеры:
Это метод, который устанавливает имя до его возвращения:
Это метод, который устанавливает имя. Вызывающая сторона не может предположить, что имя установлено, пока не будет завершено возвращенное задание (
IsCompleted
= true):Это метод, который устанавливает имя. Вызывающая сторона не может предположить, что имя установлено, пока не будет завершено возвращенное задание (
IsCompleted
= true):Q: Какой из них не принадлежит двум другим?
A: Асинхронный метод не тот, который стоит один. Тот, который стоит один - это метод, который возвращает void.
Для меня «утечка» здесь не является
async
ключевым словом; это тот факт, что метод возвращает задачу. И это не утечка; это часть прототипа и часть абстракции. Асинхронный метод, который возвращает задачу, дает то же обещание, что и синхронный метод, который возвращает задачу.Так что нет, я не думаю, что введение
async
форм само по себе является утечкой абстракции. Но вам, возможно, придется изменить прототип, чтобы он возвращал задачу, которая «просачивается» при изменении интерфейса (абстракция). И поскольку это часть абстракции, по определению это не утечка.источник
Это неплотная абстракция тогда и только тогда, когда вы не собираетесь создавать все реализующие классы для создания асинхронного вызова. Вы можете создать несколько реализаций, например, по одной для каждого типа базы данных, который вы поддерживаете, и это было бы совершенно нормально, если бы вам никогда не нужно было знать точную реализацию, используемую в вашей программе.
И хотя вы не можете строго применять асинхронную реализацию, само название подразумевает, что так и должно быть. Если обстоятельства меняются, и это может быть синхронный вызов по любой причине, тогда вам вполне может потребоваться рассмотреть вопрос об изменении имен, поэтому я советую сделать это только в том случае, если вы не думаете, что это будет очень вероятно в будущее.
источник
Вот противоположная точка зрения.
Мы не пошли от возвращения
Foo
к возвращению,Task<Foo>
потому что мы начали хотеть,Task
а не простоFoo
. Конечно, иногда мы взаимодействуем с,Task
но в большинстве реальных программ мы игнорируем это и просто используемFoo
.Более того, мы часто определяем интерфейсы для поддержки асинхронного поведения, даже когда реализация может быть или не быть асинхронной.
В сущности, интерфейс, который возвращает a,
Task<Foo>
говорит вам, что реализация может быть асинхронной, независимо от того, действительно ли она есть, даже если вам это может или не нужно. Если абстракция говорит нам больше, чем мы должны знать о ее реализации, это утечка.Если наша реализация не асинхронная, мы изменяем ее на асинхронную, а затем мы должны изменить абстракцию и все, что ее использует, это очень утечка абстракции.
Это не суждение. Как уже отмечали другие, все абстракции протекают. Этот действительно имеет большее влияние, потому что он требует волнового эффекта асинхронного ожидания в нашем коде только потому, что где-то в его конце может быть что-то, что на самом деле является асинхронным.
Это похоже на жалобу? Это не мое намерение, но я думаю, что это точное наблюдение.
Связанным моментом является утверждение, что «интерфейс не является абстракцией». То, что кратко изложил Марк Симан, было немного оскорблено.
Определение «абстракция» не является «интерфейсом», даже в .NET. Абстракции могут принимать множество других форм. Интерфейс может быть плохой абстракцией или настолько близко отражать его реализацию, что в некотором смысле это вообще не абстракция.
Но мы абсолютно используем интерфейсы для создания абстракций. Таким образом, отбросить «интерфейсы не абстракции», потому что вопрос упоминает интерфейсы и абстракции не является просветляющим.
источник
Это на
GetAllAsync()
самом деле асинхронный? Я имею в виду, что в названии есть «async», но это можно удалить. Поэтому я спрашиваю еще раз ... Разве невозможно реализовать функцию, которая возвращает а,Task<IEnumerable<User>>
которая разрешается синхронно?Я не знаю специфики типа .Net
Task
, но если невозможно реализовать функцию синхронно, то уверен, что это дырявая абстракция (таким образом), но в противном случае нет. Я же знаю , что если это было ,IObservable
а не задачи, он может быть реализован либо синхронно , либо асинхронным так ничего вне функции знает , и поэтому он не протекает этот конкретный факт.источник
Task<T>
означает асинхронный. Вы сразу получаете объект задачи, но, возможно, придется ждать