-1

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?

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
Soumya Kar
  • 11
  • 4
  • 1
    Composition, not inheritance – Jamiec Feb 28 '18 at 13:58
  • 1
    `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` - well if you'd like them all to be dealt with implicitly, you must work out what they have in common - or what you need from them. An interface is, in essence, defines what's 'common' amongst implementations. It's a contract. –  Feb 28 '18 at 14:01
  • Design the interface for the common functionality and do not include anything specific to the implementation in the interface. If you are talking about plugins which will be lazy loaded at runtime, each plugin will need different settings so the plugin will either be responsible for loading settings from a config file, or exposing its own settings class which will have to be filled by the calling application in a certain way. If you are just instantiating them at composition root, then just use DI like stated in @ScottHannen's answer below. – vgru Feb 28 '18 at 14:08

1 Answers1

1

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.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62