2012-04-03 3 views
0

Ich schreibe gerade ein Projekt, das verschiedene Arten von Objekten generieren muss, basierend auf der XML-Konfigurationsdatei.Initialisieren von Objekten mithilfe der Konfigurationsdatei zur Laufzeit

Jedes generierte Objekt ist eine Instanz einer IProvidor-Schnittstelle und muss mehrere Vorverarbeitungs- und Verarbeitungsmethoden enthalten, die in der XML-Konfigurationsdatei definiert sind. I erzeugen verschiedene Fabriken Klassen für:.

  1. die Anbieter zu schaffen (die die IProvider Schnittstelle implementieren)
  2. den Pre-Processing-Betrieb zu schaffen (ich habe IPreProcessor Schnittstelle, die alle Preprocessor Klasse implementieren müssen
  3. die gleiche für Handhabungsmethoden (IHandler Schnittstelle von mehreren Klassen implementiert werden).

Frage

Wie kann ich all dies in Runtime zu einem Objekt kombinieren?

+0

Diese Frage vage. Es ist mir unklar, was Sie genau wissen wollen. – Steven

+0

was ich suche ist das: – doubleM

+0

@Steven, was ich suche ist das: sagen wir, ich habe zwei verschiedene Providor MailProvider und FTPProvider (beide implementieren IProvider) und zwei verschiedene Handler-Klassen, ZipFilesHandler und RARFileHandle (implementieren Sie IHandler). Theoretisch habe ich vier Möglichkeiten, neue Objekte zu erstellen (MailProvider mit ZipFileHandler-Funktionalität, MailProvider mit RARFileHandler-Funktionalität, FTPProvider mit ZipFileHandler usw.). Wie kann ich das richtige Objekt in Runtime – doubleM

Antwort

3

Olivier Jacot-Desc ist absolut auf dem richtigen Weg (+1 dafür). Das einzige, was in seiner Antwort fehlt, ist das Laden der korrekten Implementierungen aus der Konfiguration.

Es gibt viele Möglichkeiten, dies zu tun, zum Beispiel, indem Sie den Typnamen in der Konfiguration speichern, aber Sie können auch einen einfacheren Ansatz wählen, z. B. einen einfachen booleschen Wert in der Konfiguration speichern.

IProvider providerX = GetProviderFromConfig(); 
IHandler handlerZ = GetHandlerFromConfig(); 
IPreProcessor preProcessorY = GetProcessorFromConfig(); 

var provider = 
    new ProviderWrapper(providerX, preProcessorY, handlerZ); 

private static IProvider GetProviderFromConfig() 
{ 
    if (ConfigurationManager.AppSettings["provider"] == "mail") 
    { 
     return new MailProvider(); 
    } 
    else 
    { 
     return new FtpProvider(); 
    } 
} 

// implement GetHandlerFromConfig just like 
// the GetProvider. 

UPDATE

Wenn Sie viele Arten haben zwischen zu schalten, den Namen des Typs Speicherung könnte eine bessere Wahl sein:

private static IProvider GetProviderFromConfig() 
{ 
    string typeName = 
     ConfigurationManager.AppSettings["provider"]; 

    Type providerType = Type.GetType(typeName); 

    return (IProvider) 
     Activator.CreateInstance(providerType); 
} 

UPDATE 2

Hier finden Sie ein Beispiel, wie Sie dies mit einem DI-Container konfigurieren können.Ich verwende Simple Injector (mit extensions), aber jeder Behälter tun (auch wenn die Art und Weise zu konfigurieren, wird pro Behälter unterscheiden):

Anmeldung:

using SimpleInjector; 
using SimpleInjector.Extensions; 

Type providerType = Type.GetType(
    ConfigurationManager.AppSettings["provider"]); 

Type handlerType = Type.GetType(
    ConfigurationManager.AppSettings["handler"]); 

Type processorType = Type.GetType(
    ConfigurationManager.AppSettings["preProcessor"]); 

var container = new Container(); 

container.Register(typeof(IProvider), providerType); 
container.Register(typeof(IHandler), handlerType); 
container.Register(typeof(IPreProcessor), processorType); 

einen Anbieter auflösen:

var provider = container.GetInstance<IPovider>(); 

Tipp: Wenn Sie Konstruktorinjektion verwenden, müssen Sie die Typen nicht per Hand verdrahten, der Container erledigt dies für Sie. Zum Beispiel, wenn Ihre MailProvider so aussieht, ist der Behälter der Lage, die benötigten Abhängigkeiten (IHandler und IPreProcessor) durch den Konstruktor zu injizieren:

public class MailProvider : IProvider 
{ 
    private readonly IHandler handler; 
    private readonly IPreProcessor preProcessor; 

    public MailProvider(IHandler handler, 
     IPreProcessor preProcessor) 
    { 
     this.handler = handler; 
     this.preProcessor = preProcessor; 
    } 

    public void SomeAction() { ... } 
} 
+2

+1 generieren. Du liegst absolut richtig. Der einfachste Weg, dies zu tun, ist die Verwendung einer 'if'- oder einer' switch'-Anweisung, um die richtige Implementierung zu erhalten. In komplexeren Szenarien könnten Sie 'Reflection' verwenden, um eine geeignete Instanz zu generieren. Möglicherweise könnte die XML-Datei sogar die Namen von Assemblies enthalten, die dynamisch geladen werden müssten. Und schließlich könnte ein IOC-Container dazu verwendet werden. –

+0

@ OlivierJacot-Descombes: Ich habe ein Beispiel mit einem DI-Container geschrieben, aber ich habe beschlossen, das zu lassen, um die Antwort so einfach wie möglich zu halten. – Steven

+0

@ OlivierJacot-Descombes und Steven, danke für deine Antworten. Das einzige Problem damit ist, wenn ich viele (in meinem Fall einige Dutzend ...) von Handlern und Preprozessoren habe, werden die if/switch-Anweisungen endlos sein. – doubleM

0

Vielleicht könnten Sie Instanzen aller dieser Klassen erstellen, wie Sie normalerweise zur Laufzeit, dann serialisieren sie in XML. Dann, wenn Sie Ihre "Konfiguration" laden möchten, müssen Sie nur dein serialisieren.

Siehe here für die Serialisierung.

2

Ich würde eine Wrapper-Klasse erstellen, die diese Schnittstellen implementiert und die Funktionalität injizieren.

Beispiel Schnittstellen

public interface IProvider 
{ 
    string ProvideSomething(int id); 
} 

public interface IPreProcessor 
{ 
    void PreProcess(string parameter); 
} 

public interface IHandler 
{ 
    void HandleSomething(); 
} 

Der Wrapper

public class ProviderWrapper : IProvider, IPreProcessor, IHandler 
{ 
    private IProvider _provider; 
    private IPreProcessor _preProcessor; 
    private IHandler _handler; 

    public ProviderWrapper(IProvider provider, IPreProcessor preProcessor, IHandler handler) 
    { 
     _provider = provider; 
     _preProcessor = preProcessor; 
     _handler = handler; 
    } 

    #region IProvider Members 

    public string ProvideSomething(int id) 
    { 
     return _provider.ProvideSomething(id); 
    } 

    #endregion 

    #region IPreProcessor Members 

    public void PreProcess(string parameter) 
    { 
     _preProcessor.PreProcess(parameter); 
    } 

    #endregion 

    #region IHandler Members 

    public void HandleSomething() 
    { 
     _handler.HandleSomething(); 
    } 

    #endregion 
} 

nun alle diese Schnittstellen implementieren würde, können Sie eine ProviderWrapper mit der erforderlichen Funktionalität entsprechend der Konfigurationsdatei instanziiert und andere die Schnittstelle Implementierungen kombinieren .

var provider = new ProviderWrapper(providerX, preProcessorY, handlerZ);