重构静态类以使用dependency injection

我们需要在具有静态方法的代码中使用非托管库。 我想在我的代码中将库操作作为依赖项引入。 除了具有静态方法之外,库还具有初始化方法和设置方法,两者都是全局的。 因此,我不能将它包装在实例类中,因为如果一个实例更改了设置,则所有其他实例都将受到影响,如果一个实例初始化,则所有其他实例将重新初始化。

我想把它作为一个单独的类引入。 这样它将在一个实例类中,但只有一个实例,因此我不必担心更改设置或初始化。 您如何看待这种方法? 我对dependency injection模式很新,我不确定单例模式是否是一个好的解决方案? 您对类似案例的解决方案是什么?

编辑:初始化也是一个参数,所以我不能只是锁定方法调用,并在每次调用时重新初始化和更改设置。

编辑2:以下是一些方法的签名:

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) 

如果您只需要在启动时设置一次设置,那么我建议创建一个非静态包装类,它在自己的静态构造函数中完成静态类的所有初始化。 这样你可以放心,它只会发生一次:

 public class MyWrapper { public MyWrapper() { // Do any necessary instance initialization here } static MyWrapper() { UnManagedStaticClass.Initialize(); UnManagedStaticClass.Settings = ...; } public void Method1() { UnManagedStaticClass.Method1(); } } 

但是,如果您每次调用它时都需要更改设置,并且希望使实例具有线程安全性,那么我建议您锁定静态对象,这样您就不会意外地覆盖静态设置。仍然被另一个线程使用:

 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(); } } } 

如果您需要将初始化参数传递到类的实例构造函数中,那么您也可以通过使用静态标志字段来执行此操作:

 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(); } } } 

如果你还需要每次重新初始化,但是你担心性能,因为重新初始化太慢,那么唯一的另一个选择(在可怕的单例之外)是自动检测你是否需要重新初始化和只在必要时才这样做。 至少那时,唯一的时间是两个线程同时使用两个不同的实例。 你可以这样做:

 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(); } } } 

我将使用无状态服务类,并使用每个方法调用传递静态类的状态信息。 在不知道你的任何细节的情况下,我将用ac#static class来展示另一个例子。

 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 } } } 

你需要填写一些空白,但希望你能得到这个想法。 关键是要将静态对象上的状态设置为所需的最短时间,并锁定整个时间段的访问权限,这样其他调用者就不会受到全局状态更改的影响。 您必须创建此类的新实例才能使用它,因此它是完全可注入和可测试的(除了提取界面的步骤,我为简洁起见跳过了这一步骤)。

这里有很多实现方案。 例如,如果您必须经常更改LegacyCodeState,但仅限于少数特定状态,则可能会有重载来执行管理这些状态的工作。

编辑

这在很多方面优于单例,最重要的是你将无法累积并耦合到全局状态:如果它是静态类的唯一入口点,则会将全局状态转换为非全局状态。 但是,如果您最终需要单例,可以通过在此处封装构造函数来轻松切换。

 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 } } }