I'm a programmer working on a project using .Net 4.0 and trying to figure out the best way to combine a loosely coupled and extensible design with the fact that the same logical operation may sometimes be performed asynchronously and sometimes only synchronously (every implementation that supports async also supports sync, but not the other way around). In this project, every such logical operation is represented by an interface (the loose coupling and extensibility demands are high). Let's focus my question around a specific interface: IDataDictLoader
.
This interface is responsible for loading an object called DataDict
. There are two possible implementations for this interface: one called LocalDataDictLoader
which uses local dlls and performs the operation synchronously (returning DataDict
), and the other called WebServiceDataDictLoader
which uses a web service and performs the operation asynchronously (returning Task<DataDict>
) or synchronously. A value in a configuration file determines the implementation to create - if the value is "Local" then LocalDataDictLoader
is created, if the value is "WebService" then WebServiceDataDictLoader
is created. The creation part uses reflection guided by conventions, in the function IDataDictLoader CreateLoader(string configValue)
. The code calling that function and consuming the interface does not know up ahead what the implementation will be, it doesn't have access to the configuration value even it wanted to know. The question is how to design the interface? As of now I thought of a number of options:
- Have two methods on the interface, one sync and one async, and have async wrapper in
LocalDataDictLoader
for the sync version. As suggested by Stephen Toub here, that's not a recommended solution. - Do as suggested by Josh's answer to the question here, in his IIMViewModelDL example, such that the interface will have a void method with callback parameter. While that solution will hide implementation details from client code, it feels to me like it has asynchronous invocation semantics, using a callback. In my opinion it's equivalent to always returning a
Task<DataDict>
and have the synchronous implementation do the loading synchronously then retuning a completed task with the result usingTaskCompletionSource<DataDict>
. This is wrong as option 1, for same reasons. - Split the interface to two interfaces:
IDataDictLoader
for synchronous,IAsyncDataDictLoader : IDataDictLoader
for asynchronous and synchronous. This solution provides client code with knowledge if the operation may be performed asynchronously or not, it doesn't "lie" about its nature. The downside is now the client code, which calledCreateLoader(string configValue)
, will now have to use as/is conditional coding style to know it recieved anIDataDictLoader
which is actually anIAsyncDataDictLoader
or not.
So, what would you do to balance between demands on loose coupling and extensibility, while exposing true nature of whether the configured implementation supports asynchronous or not?