12

Wir müssen eine unmanaged Bibliothek in unserem Code verwenden, die statische Methoden hat. Ich möchte die Bibliotheksoperation als Abhängigkeit in meinen Code einführen. Abgesehen von statischen Methoden besitzt die Bibliothek eine Initialisierungsmethode und eine Einstellungsmethode, beide sind global. Ich kann das also nicht einfach in eine Instanzklasse einbinden, denn wenn eine Instanz eine Einstellung ändert, sind alle anderen Instanzen betroffen, und wenn eine Instanz initialisiert wird, werden alle anderen Instanzen neu initialisiert.Refactoring einer statischen Klasse zur Verwendung mit Dependency-Injektion

Ich dachte darüber nach, es als Singleton-Klasse einzuführen. Auf diese Weise wird es in einer Instanz-Klasse sein, aber es wird nur eine Instanz geben, so dass ich mich nicht darum kümmern muss, die Einstellungen oder die Initialisierung zu ändern. Was denkst du über diesen Ansatz? Ich bin ziemlich neu im Abhängigkeitsinjektionsmuster und ich bin mir nicht sicher, ob das Singleton-Muster eine gute Lösung ist. Was wäre Ihre Lösung für einen ähnlichen Fall?

Bearbeiten: Die Initialisierung nimmt auch einen Parameter, so kann ich nicht einfach die Methodenaufrufe sperren und neu initialisieren und Einstellungen jedes Mal ändern, wenn es aufgerufen wird.

Edit 2: Hier sind die Unterschriften von einigen Methoden:

public static void Initialize(int someParameter) 
// Parameter can only be changed by re-initalization which 
// will reset all the settings back to their default values. 

public static float[] Method1(int someNumber, float[] someArray) 

public static void ChangeSetting(string settingName, int settingValue) 
+0

Die Klasse, die Sie umbrechen möchten, ist es statische oder nur einige statische Methoden? Muss Ihr Wrapper zusätzliche Funktionen bereitstellen? – JMan

+0

Ihre Idee scheint vernünftig – sll

+0

@ Jeroen es muss einige Methoden Überladungen zur Verfügung stellen. Alle Methoden in der Bibliothek verwenden Arrays und ich möchte sie in Klassen einbinden. Die Bibliothek ist in C++ geschrieben und die Klasse, die ich umbrechen möchte, ist statisch, nicht nur die Funktionen. – hattenn

Antwort

8

Wenn Sie die Einstellungen nur einmal beim Start einstellen müssen, dann würde ich empfehlen, eine nicht-statische Wrapper-Klasse zu machen, die tun die gesamte Initialisierung der statischen Klasse in einem eigenen statischen Konstruktor. So können Sie sicher sein, dass es nur einmal passiert:

public class MyWrapper 
{ 
    public MyWrapper() 
    { 
     // Do any necessary instance initialization here 
    } 

    static MyWrapper() 
    { 
     UnManagedStaticClass.Initialize(); 
     UnManagedStaticClass.Settings = ...; 
    } 

    public void Method1() 
    { 
     UnManagedStaticClass.Method1(); 
    } 
} 

Wenn Sie jedoch jedes Mal die Einstellungen ändern, müssen Sie es nennen, und Sie möchten Ihre Instanzen Thread-sicher machen, dann würde ich empfehlen, Sperren auf ein statisches Objekt, so dass Sie nicht versehentlich die statischen Einstellungen überschreiben Sie, während sie noch in Verwendung von einem anderen Thread sind:

public class MyWrapper 
{ 
    public MyWrapper() 
    { 
     // Do any necessary instance initialization here 
    } 

    static MyWrapper() 
    { 
     UnManagedStaticClass.Initialize(); 
    } 

    static object lockRoot = new Object(); 

    public void Method1() 
    { 
     lock (lockRoot) 
     { 
      UnManagedStaticClass.Settings = ...; 
      UnManagedStaticClass.Method1(); 
     } 
    } 
} 

Wenn Sie Initialisierungsparameter in Ihre Klasse Instanzkonstruktors übergeben müssen, dann könnte man tun Sie das auch, indem Sie ein statisches Markierungsfeld haben:

public class MyWrapper 
{ 
    public MyWrapper(InitParameters p) 
    { 
     lock (lockRoot) 
     { 
      if (!initialized) 
      { 
       UnManagedStaticClass.Initialize(p); 
       initialized = true; 
      } 
     } 
    } 

    static bool initialized = false; 
    static object lockRoot = new Object(); 

    public void Method1() 
    { 
     lock (lockRoot) 
     { 
      UnManagedStaticClass.Settings = ...; 
      UnManagedStaticClass.Method1(); 
     } 
    } 
} 

Wenn Sie auch jedes Mal neu initialisieren müssen, sich aber Sorgen um die Leistung machen, da die Neuinitialisierung zu langsam ist, dann ist die einzige andere Option (außerhalb des gefürchteten Singletons) die automatische Erkennung, falls Sie sie benötigen neu initialisieren und nur tun, wenn es notwendig ist. Zumindest dann, wenn zwei Threads gleichzeitig zwei verschiedene Instanzen verwenden. Man könnte es wie folgt tun:

public class MyWrapper 
{ 
    public MyWrapper(InitParameters initParameters, Settings settings) 
    { 
     this.initParameters = initParameters; 
     this.settings = settings; 
    } 

    private InitParameters initParameters; 
    private Settings settings; 
    static MyWrapper currentOwnerInstance; 
    static object lockRoot = new Object(); 

    private void InitializeIfNecessary() 
    { 
     if (currentOwnerInstance != this) 
     { 
      currentOwnerInstance = this; 
      UnManagedStaticClass.Initialize(initParameters); 
      UnManagedStaticClass.Settings = settings; 
     } 
    } 

    public void Method1() 
    { 
     lock (lockRoot) 
     { 
      InitializeIfNecessary(); 
      UnManagedStaticClass.Method1(); 
     } 
    } 
} 
+0

Es geht nicht nur um Thread-Sicherheit. Auf diese Weise werden alle anderen Instanzen ebenfalls beeinflusst, wenn eine Instanz die Einstellungen ändert. – hattenn

+0

Nein, andere Instanzen sind nicht betroffen, solange Sie die Einstellungen immer zuerst vornehmen (innerhalb des Sperrblocks). –

+0

Und Initialisierung nimmt auch einen Parameter, mit dieser Methode sollte jedes Mal, wenn ich es anrufe, es neu initialisiert werden und seine Einstellungen sollten zurückgesetzt werden, was wahrscheinlich alle diese Vorgänge drastisch verlangsamen wird. Sie werden diese Bibliothek ausgiebig nutzen. – hattenn

1

ich staatenlos Serviceklasse verwenden würde, und mit jedem Methodenaufruf in Zustand Info für die statische Klasse übergeben. Ohne irgendwelche Details Ihrer Klasse zu kennen, werde ich nur ein weiteres Beispiel mit einer C# statischen Klasse zeigen.

public static class LegacyCode 
{ 
    public static void Initialize(int p1, string p2) 
    { 
     //some static state 
    } 
    public static void ChangeSettings(bool p3, double p4) 
    { 
     //some static state 
    } 
    public static void DoSomething(string someOtherParam) 
    { 
     //execute based on some static state 
    } 
} 

public class LegacyCodeFacadeService 
{ 
    public void PerformLegacyCodeActivity(LegacyCodeState state, LegacyCodeParams legacyParams) 
    { 
     lock (_lockObject) 
     { 
      LegacyCode.Initialize(state.P1, state.P2); 
      LegacyCode.ChangeSettings(state.P3, state.P4); 
      LegacyCode.DoSomething(legacyParams.SomeOtherParam); 
      //do something to reset state, perhaps 
     } 
    } 
} 

Sie müssen in die Lücken ein wenig füllen, aber hoffentlich bekommen Sie die Idee. Der Punkt besteht darin, den Status für das statische Objekt für die erforderliche Mindestdauer festzulegen und den Zugriff darauf für die gesamte Zeit zu sperren, sodass andere Anrufer nicht von Ihrer globalen Statusänderung betroffen sein können. Sie müssen neue Instanzen dieser Klasse erstellen, um sie zu verwenden, so dass sie vollständig injizierbar und testbar ist (außer dem Schritt, eine Schnittstelle zu extrahieren, die ich aus Platzgründen übersprungen habe).

Es gibt viele Optionen in der Umsetzung hier.Wenn Sie beispielsweise LegacyCodeState häufig, aber nur in einer bestimmten Anzahl von Zuständen ändern müssen, können Überladungen die Verwaltung dieser Status übernehmen.

EDIT

Dies zu einem Singleton in vielerlei Hinsicht vorzuziehen ist, was am wichtigsten ist, dass Sie nicht in der Lage sein wird, zu den globalen Zustand zu akkumulieren und Paar: Schaltet globalen Zustand in nicht-globalen Zustand wenn es der einzige Einstiegspunkt zu Ihrer statischen Klasse ist. Für den Fall, dass Sie jedoch einen Singleton benötigen, können Sie den Wechsel durch Kapselung des Konstruktors vereinfachen.

public class LegacyCodeFacadeService 
{ 
    private LegacyCodeFacadeService() { } 

    public static LegacyCodeFacadeService GetInstance() 
    { 
     //now we can change lifestyle management strategies later, if needed 
     return new LegacyCodeFacadeService(); 
    } 

    public void PerformLegacyCodeActivity(LegacyCodeState state, LegacyCodeParams legacyParams) 
    { 
     lock (_lockObject) 
     { 
      LegacyCode.Initialize(state.P1, state.P2); 
      LegacyCode.ChangeSettings(state.P3, state.P4); 
      LegacyCode.DoSomething(legacyParams.SomeOtherParam); 
      //do something to reset state, perhaps 
     } 
    } 
} 
+0

Ich möchte das Objekt nicht oft neu initialisieren, und auch so wird es (so weit ich das verstehe) unmöglich parallelisieren, falls es benötigt wird. Danke für die Eingabe. – hattenn

+0

Wenn Sie ein Singleton verwenden, können Sie in der Zukunft nicht * parallelisieren *. Nur das Zurücksetzen der Einstellungen kann das * Problem * lösen. –

+0

Sie können keine Aktionen parallelisieren, die in einem anderen globalen Zustand ausgeführt werden müssen. Ist die Initialisierung teuer? Wenn nicht, brauchen Sie sich nicht darum zu kümmern. Wenn ja, nur init, wenn sich ein Init-Parameter ändert. – tallseth