Второго подхода
У меня есть семейство приложений, которые предлагают расширяемые (т.е. не фиксировано) множество переменных, которые могут быть использованы различными модулями.инъекция Динамической зависимости
Примеров являются:
- источника для журнала событий
- Источника вычисления результатов
- источника для использования системных ресурсов
- источника для показателей Performace
- ...
Плагины могут использовать любую комбинацию как таковые.
Примеры плагинов могут быть:
- Регистратор пользовательских ошибок, используя 1.
- A модуль пользовательских статистики, используя 2.
- Инструмент Performace используя 3. и 4.
Чего я хочу достичь:
- presen t список плагинов, которые можно использовать, учитывая набор переменных, присутствующих в этом приложении (при отсутствии источника событий журнала вы не сможете выбрать собственный журнал ошибок).
- получить простой и безопасный способ передачи переменных в плагины, чтобы избежать ошибок времени выполнения из-за отсутствия переменных.
Бонус будет заключаться в том, чтобы позволить плагинам необязательно требовать переменную, например. плагин, который требует 4. и необязательно использует 3. если он доступен (но также доступен в противном случае).
Первый подход
Я хочу осуществить своего рода «инъекции динамической зависимости». Позвольте мне объяснить это с использованием прецедента.
Я строю набор библиотек, которые будут использоваться для семейства приложений. Каждое приложение может предоставить различный набор переменных, доступных некоторым «обработчикам», нуждающимся в этих переменных. В зависимости от конкретных доступных переменных необходимо определить количество доступных обработчиков, поскольку обработчики могут использоваться только в том случае, если они имеют доступ ко всем требуемым переменным. Также я ищу способ сделать вызов максимально безопасным. Время компиляции, вероятно, не возможно, но «проверить один раз, никогда не сработает после», будет хорошо.
Ниже представлен первый эскиз. На этом этапе все еще можно изменить.
class DynamicDependencyInjectionTest
{
private ISomeAlwaysPresentClass a;
private ISomeOptionalClass optionA;
private ISomeOtherOptionalClass optionB;
private ISomeMultipleOption[] multi;
private IDependentFunction dependentFunction;
void InvokeDependency()
{
// the number of available dependencies varies.
// some could be guaranteed, others are optional, some maybe have several instances
var availableDependencies = new IDependencyBase[] {a, optionA, optionB}.Concat(multi).ToArray();
//var availableDependencies = new IDependencyBase[] { a };
//var availableDependencies = new IDependencyBase[] { a, optionA }.ToArray();
//var availableDependencies = new IDependencyBase[] { a, optionB }.ToArray();
//var availableDependencies = new IDependencyBase[] { a , multi.First() };
//ToDo
// this is what I want to do
// since we checked it before, this must always succeed
somehowInvoke(dependentFunction, availableDependencies);
}
void SetDependentFunction(IDependentFunction dependentFunction)
{
if (! WeCanUseThisDependentFunction(dependentFunction))
throw new ArgumentException();
this.dependentFunction = dependentFunction;
}
private bool WeCanUseThisDependentFunction(IDependentFunction dependentFunction)
{
//ToDo
//check if we can fulfill the requested dependencies
return true;
}
/// <summary>
/// Provide a list which can be used by the user (e.g. selected from a combobox)
/// </summary>
IDependentFunction[] AllDependentFunctionsAvailableForThisApplication()
{
IDependentFunction[] allDependentFunctions = GetAllDependentFunctionsViaReflection();
return allDependentFunctions.Where(WeCanUseThisDependentFunction).ToArray();
}
/// <summary>
/// Returns all possible candidates
/// </summary>
private IDependentFunction[] GetAllDependentFunctionsViaReflection()
{
var types = Assembly.GetEntryAssembly()
.GetTypes()
.Where(t => t.IsClass && typeof (IDependentFunction).IsAssignableFrom(t))
.ToArray();
var instances = types.Select(t => Activator.CreateInstance(t) as IDependentFunction).ToArray();
return instances;
}
private void somehowInvoke(IDependentFunction dependentFunction, IDependencyBase[] availableDependencies)
{
//ToDo
}
}
// the interfaces may of course by changed!
/// <summary>
/// Requires a default constructor
/// </summary>
interface IDependentFunction
{
void Invoke(ISomeAlwaysPresentClass a, IDependencyBase[] dependencies);
Type[] RequiredDependencies { get; }
}
interface IDependencyBase { }
interface ISomeAlwaysPresentClass : IDependencyBase { }
interface ISomeOptionalClass : IDependencyBase { }
interface ISomeOtherOptionalClass : IDependencyBase { }
interface ISomeMultipleOption : IDependencyBase { }
class BasicDependentFunction : IDependentFunction
{
public void Invoke(ISomeAlwaysPresentClass a, IDependencyBase[] dependencies)
{
;
}
public Type[] RequiredDependencies
{
get { return new[] {typeof(ISomeAlwaysPresentClass)}; }
}
}
class AdvancedDependentFunction : IDependentFunction
{
public void Invoke(ISomeAlwaysPresentClass a, IDependencyBase[] dependencies)
{
;
}
public Type[] RequiredDependencies
{
get { return new[] { typeof(ISomeAlwaysPresentClass), typeof(ISomeOptionalClass) }; }
}
}
class MaximalDependentFunction : IDependentFunction
{
public void Invoke(ISomeAlwaysPresentClass a, IDependencyBase[] dependencies)
{
;
}
public Type[] RequiredDependencies
{
// note the array in the type of ISomeMultipleOption[]
get { return new[] { typeof(ISomeAlwaysPresentClass), typeof(ISomeOptionalClass), typeof(ISomeOtherOptionalClass), typeof(ISomeMultipleOption[]) }; }
}
}
Как следует из ответа Марка, вы должны уважать принцип KISS и не пытаться изобретать колесо с каким-то неясным DI. Подумайте о тех, кто прочитает ваш код. Даже вы через несколько месяцев можете получить головные боли, чтобы понять, что вы намереваетесь в первую очередь ... – Fab