Recently I was learning about composition in object oriented programming. In many articles about it, there's an example of FileStore class (or however you call it) that implements interface like:
interface IStore
{
void Save(int id, string data);
string Read(int id);
}
FileStore class implements this interface and everything is fine. It can save and read files. Now, tutorial articles often say that FileStore can be easily exchanged to, for example, SqlStore, because it can implement the same interface. This way client classes can store to SQL instead of filesystem, just by injecting SqlStore instead of FileStore. It sounds nice and everything, but when I actually thought about it, I don't really know how to implement it. Database operations are good candidates to be done asynchronously. In order to use this fact, I would need to return a Task instead of just string in Read(int id) method. Also Save(int id, string data) could return a Task. With this in mind, I cannot really use IStore for my SqlStore class.
One solution I see is to make IStore like this:
interface IStore
{
Task Save(int id, string data);
Task<string> Read(int id);
}
Then, my FileStore class would need to be changed a little bit, it would use Task.FromResult(...).
This solution seems inconvenient to me, because FileStore has to pretend some asynchronous characteristics.
What is the solution that you would propose? I'm curious, because in tutorials everything always sound easy and doable, but when it comes to actual programming, things get complicated.