Let's say I am defining a browser implementation class for my application:
class InternetExplorerBrowser : IBrowser {
private readonly string executablePath = @"C:\Program Files\...\...\ie.exe";
...code that uses executablePath
}
This might at first glance to look like a good idea, as the executablePath
data is near the code that will use it.
The problem comes when I try to run this same application on my other computer, that has a foreign-language OS: executablePath
will have a different value.
I could solve this through an AppSettings singleton class (or one of its equivalents) but then no-one knows my class is actually dependent on this AppSettings class (which goes against DI ideias). It might pose a difficulty to Unit-Testing, too.
I could solve both problems by having executablePath
being passed in through the constructor:
class InternetExplorerBrowser : IBrowser {
private readonly string executablePath;
public InternetExplorerBrowser(string executablePath) {
this.executablePath = executablePath;
}
}
but this will raise problems in my Composition Root
(the startup method that will do all the needed classes wiring) as then that method has to know both how to wire things up and has to know all these little settings data:
class CompositionRoot {
public void Run() {
ClassA classA = new ClassA();
string ieSetting1 = "C:\asdapo\poka\poskdaposka.exe";
string ieSetting2 = "IE_SETTING_ABC";
string ieSetting3 = "lol.bmp";
ClassB classB = new ClassB(ieSetting1);
ClassC classC = new ClassC(B, ieSetting2, ieSetting3);
...
}
}
which will turn easily a big mess.
I could turn this problem around by instead passing an interface of the form
interface IAppSettings {
object GetData(string name);
}
to all the classes that need some sort of settings. Then I could either implement this either as a regular class with all the settings embedded in it or a class that reads data off a XML file, something along the lines. If doing this, should I have a general AppSettings class instance for the whole system, or have an AppSettings class associated to each class that might need one? That certainly seems like a bit of an overkill. Also, have all the application setings in the same place makes it easy to look and see what might be all the changes I need to do when tryign to move the program to different platforms.
What might be the best way to approach this common situation?
Edit:
And what about using an IAppSettings
with all its settings hardcoded in it?
interface IAppSettings {
string IE_ExecutablePath { get; }
int IE_Version { get; }
...
}
This would allow for compile-time type-safety. If I saw the interface/concrete classes grow too much I could create other smaller interfaces of the form IMyClassXAppSettings
. Would it be a burden too heavy to bear in med/big sized projects?
I've also reading about AOP and its advantages dealing with cross-cutting-concerns (I guess this is one). Couldn't it also offer solutions to this problem? Maybe tagging variables like this:
class InternetExplorerBrowser : IBrowser {
[AppSetting] string executablePath;
[AppSetting] int ieVersion;
...code that uses executablePath
}
Then, when compiling the project we'd also have compile time safety (having the compiler check that we actually implemented code that would weave in data. This would, of course, tie our API to this particular Aspect.
Main
method or anApplication_Start
routine is the piece of code closest to and most knowledgeable about the configuration mechanism, so it's the most sensible place to put configuration logic, whether that involves parsing command-line arguments, reading a configuration file, or obtaining information about the execution context. – Gilburt