Я программист, работающий над проектом, использующим .Net 4.0, и пытаюсь найти лучший способ совместить слабосвязанный и расширяемый дизайн с тем фактом, что одна и та же логическая операция может иногда выполняться асинхронно, а иногда только синхронно (каждая реализация, которая поддерживает асинхронность, также поддерживает синхронизацию, но не наоборот). В этом проекте каждая такая логическая операция представлена интерфейсом (требования к слабой связи и расширяемости высоки). Давайте сосредоточим мой вопрос на конкретном интерфейсе: IDataDictLoader
. Этот интерфейс отвечает за загрузку объекта с именем DataDict
. Существует две возможные реализации этого интерфейса: одна называется LocalDataDictLoader
, которая использует локальные библиотеки DLL и выполняет операцию синхронно (возвращая DataDict
), а другая называется WebServiceDataDictLoader
, которая использует веб-службу и выполняет операцию . асинхронно (возвращая Task<DataDict>
) или синхронно. Значение в файле конфигурации определяет реализацию, которую нужно создать: если значение «Local», то создается LocalDataDictLoader
, если значение «WebService», то создается WebServiceDataDictLoader
. Часть создания использует отражение, руководствуясь соглашениями, в функции IDataDictLoader CreateLoader(string configValue)
. Код, вызывающий эту функцию и использующий интерфейс, не знает заранее, какой будет реализация, у него нет доступа к значению конфигурации, даже если он хотел знать. Вопрос в том, как спроектировать интерфейс? На данный момент я рассматривал несколько вариантов:
- Имейте два метода в интерфейсе, один синхронный и один асинхронный, и используйте асинхронную оболочку в
LocalDataDictLoader
для версии синхронизации. Как предложил Стивен Туб здесь, это не рекомендуемое решение. - Сделайте так, как предложено Джошем в ответе на вопрос >здесь, в его примере IIMViewModelDL, так что интерфейс будет иметь метод void с параметром обратного вызова. Хотя это решение будет скрывать детали реализации от клиентского кода, мне кажется, что оно имеет семантику асинхронного вызова с использованием обратного вызова. На мой взгляд, это эквивалентно постоянному возврату
Task<DataDict>
и синхронной реализации, выполняющей синхронную загрузку, а затем повторной настройке завершенной задачи с результатом с использованиемTaskCompletionSource<DataDict>
. Это неправильно, как вариант 1, по тем же причинам. - Разделите интерфейс на два интерфейса:
IDataDictLoader
для синхронного,IAsyncDataDictLoader : IDataDictLoader
для асинхронного и синхронного. Это решение предоставляет клиентскому коду информацию о том, может ли операция выполняться асинхронно или нет, оно не «лжет» о ее природе. Недостатком является то, что теперь клиентский код, который называетсяCreateLoader(string configValue)
, теперь должен будет использовать стиль условного кодирования as/is, чтобы знать, что он получилIDataDictLoader
, который на самом деле являетсяIAsyncDataDictLoader
или нет.
Итак, что бы вы сделали, чтобы сбалансировать требования к слабой связи и расширяемости, раскрывая при этом истинную природу того, поддерживает ли сконфигурированная реализация асинхронность или нет?