I am currently working on a C# Plugin based design where the Plugin API methods will have access to a Context object which will contain the relevant information required for the method to work. In the current implementation, a file path is configured as one of the Context properties as it has to be supplied to one of the primary methods as it parses the file and loads an information hierarchy which other methods in the API will use. But the flexibility that is desired in the Context interface is to be able to accommodate a wide variety of data sources rather than only a file based data source e.g. at a later point of time, the data that is coming in the form of a file now, might be replaced by a DB based data source or a network stream, or a JSON data returned by a web service invoke. The current interface is as:
public interface IFlowContext : IPluginContext
{
string FlowFilePath { get; set; }
string FlowImportFilePath { get; set; }
}
I am having a bit of difficulty in figuring out a sufficiently generic data source based interface definition to handle the desired flexibility.
Does anybody has any ideas?
The answer is don't try to create one interface that's "generic" enough for the settings of your current implementation and the settings needed by future implementations. Each interface should be written for the class that depends on it. You might still find some commonality where you get to reuse some of those interfaces. For example, you could have a different implementation of the class that depends on file paths. But there's zero reason why, if two different classes depend on two different data sources, one interface should be able to provide settings to both of them.
Not to be confused - whatever it is that these file paths support, that should be a reusable interface. But those implementations may require different settings. This one requires file paths. Another might require a database connection string or something different. But there's no need for those implementations to depend on some common interface. They should all depend on an interface that "describes" what each individual implementation requires.
This relates to the Interface Segregation Principle. If an implementation requires file paths, it should depend on an interface that provides file paths. But if an implementation does not require file paths then it shouldn't depend on an interface that provides them.
To illustrate:
public interface IGetsSomeData
{
Data GetSomeData();
}
public class GetsSomeDataFromAFile : IGetsSomeData
{
private readonly IFilePathSettings _filePathSettings;
public GetsSomeDataFromAFile(IFilePathSettings filePathSettings)
{
_filePathSettings = filePathSettings;
}
public DataGetSomeData()
{
// read data from a file using a file path
}
}
public class GetsSomeDataFromSql : IGetsSomeData
{
private readonly IDatabaseSettings _databaseSettings;
public GetsSomeDataFromAFile(IDatabaseSettings databaseSettings)
{
_databaseSettings = databaseSettings;
}
public DataGetSomeData()
{
// execute a SQL query using a connection string
}
}
IGetsSomeData
is a common interface. But the implementations don't need to share dependencies. Maybe the SQL implementation doesn't even need an interface - it might just need a connection string.