Допустим, я определяю класс реализации браузера для моего приложения:
class InternetExplorerBrowser : IBrowser {
private readonly string executablePath = @"C:\Program Files\...\...\ie.exe";
...code that uses executablePath
}
На первый взгляд это может показаться хорошей идеей, поскольку данные executablePath
находятся рядом с кодом, который будет их использовать.
Проблема возникает, когда я пытаюсь запустить это же приложение на другом компьютере с операционной системой на иностранном языке: executablePath
будет иметь другое значение.
Я мог бы решить эту проблему с помощью одноэлементного класса AppSettings (или одного из его эквивалентов), но тогда никто не знал, что мой класс фактически зависит от этого класса AppSettings (что противоречит идеям DI). Это также может создать трудности для юнит-тестирования.
Я мог бы решить обе проблемы, передав executablePath
через конструктор:
class InternetExplorerBrowser : IBrowser {
private readonly string executablePath;
public InternetExplorerBrowser(string executablePath) {
this.executablePath = executablePath;
}
}
но это вызовет проблемы в моем Composition Root
(метод запуска, который будет выполнять все необходимые подключения классов), так как тогда этот метод должен знать и как соединять вещи, и должен знать все эти маленькие данные настроек:
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);
...
}
}
, который легко превратится в большой беспорядок.
Я мог бы решить эту проблему, передав вместо этого интерфейс вида
interface IAppSettings {
object GetData(string name);
}
для всех классов, которые нуждаются в каких-то настройках. Затем я мог бы реализовать это либо как обычный класс со всеми встроенными в него настройками, либо как класс, который считывает данные из файла XML, что-то в этом роде. При этом следует ли иметь общий экземпляр класса AppSettings для всей системы или иметь класс AppSettings, связанный с каждым классом, который может понадобиться? Это, конечно, кажется излишним. Кроме того, наличие всех настроек приложения в одном месте облегчает поиск и просмотр возможных изменений, которые необходимо выполнить при попытке перенести программу на другие платформы.
Как лучше всего подойти к этой общей ситуации?
Edit:
А как насчет использования IAppSettings
со всеми его настройками в нем?
interface IAppSettings {
string IE_ExecutablePath { get; }
int IE_Version { get; }
...
}
Это позволит обеспечить безопасность типов во время компиляции. Если бы я увидел, что интерфейс / конкретные классы слишком сильно растут, я мог бы создать другие меньшие интерфейсы вида IMyClassXAppSettings
. Будет ли это слишком тяжелым бременем для проектов среднего / большого размера?
Я также читал об АОП и его преимуществах, касающихся сквозных проблем (я думаю, что это один из них). Разве он не может предложить решения этой проблемы? Может быть, пометить переменные следующим образом:
class InternetExplorerBrowser : IBrowser {
[AppSetting] string executablePath;
[AppSetting] int ieVersion;
...code that uses executablePath
}
Затем, при компиляции проекта у нас также была бы безопасность времени компиляции (если бы компилятор проверил, что мы фактически реализовали код, который будет переплетаться в данных. Это, конечно, привязало бы наш API к этому конкретному аспекту.