![](/img/trans.png)
[英]Invoke method of the control in context of its thread from separate static class
[英]Refactoring a static class to separate its interface from implementation
我正在開發一個基於.NET的應用程序,其中某些核心應用程序類僅使用靜態方法進行設計。
用法示例:
// static access.
Parameters.GetValue("DefaultTimeout");
// static access.
Logger.Log("This is an important message!");
那里已經有使用這些靜態方法的代碼,因此無法更改此“接口”。
這些類當前未實現任何接口。 我希望能夠將這些類的實際實現與它們的接口分開。
進行此重構的原因是,這些對象將在AppDomain邊界中使用。 我希望能夠注入一個“代理”對象,該對象在非main-appdomain上將調用其他一些實現,而不是默認的實現。
綜上所述,我的問題是:
如何通過僅靜態訪問基於接口的設計輕松地轉換對象,以便在需要時可以替換其實現(但保持靜態訪問)。
重構后,應如何/何時進行非默認實現的實際注入?
免責聲明:以下建議基於不更改主叫方的重要性。 我並不是說這是最佳選擇,只是我認為這是合適的。
無法在靜態成員上具有接口,因此,如果您不想更改調用代碼,則可能必須保留靜態。 就是說,您可以簡單地讓您的靜態類將接口包裝在其中,因此靜態類本身沒有任何實現-將所有調用委派給該接口。
這一切都意味着您可以將靜態類和任何調用它的代碼保留在原處。 這就像將靜態類視為接口(或協定)一樣,但是可以根據情況在內部將實現換出。
這也意味着您的接口可以具有與靜態類不同的簽名,因為該接口不必符合調用代碼的期望-基本上,它將把您的靜態類轉變為Bridge 。
簡而言之:使用靜態構造函數以解決此接口的給定實現。
靜態通常是每個AppDomain的(除非用ThreadStaticAttribute
裝飾,然后是每個AppDomain / thread的),所以您可以基於當前AppDomain確定您所在的位置以及所需的實現(每次在AppDomain中首次使用靜態時,都會調用靜態構造函數) )。 這意味着,一旦構造完成,該特定靜態類的包裝實現將在AppDomain的時間內保留(盡管您可以實現刷新實現的方法)。
負責此操作的代碼可以在靜態類中,也可以使接口實現之一只是AppDomain類型的代理管理器。 交叉AppDomain調用的任何類型都需要繼承MarshalByRefObject
。
http://msdn.microsoft.com/en-us/library/ms173139.aspx
在另一個AppDomain中的類型的CreateInstance
樣品申請
您應該只能夠將其復制並粘貼到新的控制台應用程序中。 這是在為默認的AppDomain注冊一個實現,為用戶制作的AppDomains注冊一個實現。 默認值只是創建該接口的遠程實現(在另一個AppDomain中)。 只是為了演示“每個AppDomain靜態”的想法,遠程實現將委派給非默認域的另一個實現。
您可以隨時更改實現,您只需更改靜態類構造函數即可(決定選擇哪種實現)。 請注意,您無需更改Main
方法(在這種情況下為我們的調用代碼)。
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine(Parameters.GetValue(""));
Console.Read();
}
}
static class Parameters
{
private static IParameterProvider _provider;
static Parameters()
{
if (AppDomain.CurrentDomain.IsDefaultAppDomain())
{
_provider = new ParameterProviderProxy(AppDomain.CreateDomain(Guid.NewGuid().ToString()));
}
else
{
// Breakpoint here to see the non-default AppDomain pick an implementation.
_provider = new NonDefaultParameterProvider();
}
}
public static object GetValue(string name)
{
return _provider.GetValue(name);
}
}
interface IParameterProvider
{
object GetValue(string name);
}
class CrossDomainParameterProvider : MarshalByRefObject, IParameterProvider
{
public object GetValue(string name)
{
return Parameters.GetValue(name);
}
}
class NonDefaultParameterProvider : IParameterProvider
{
public object GetValue(string name)
{
return AppDomain.CurrentDomain.FriendlyName;
}
}
class ParameterProviderProxy : IParameterProvider
{
private IParameterProvider _remoteProvider;
public ParameterProviderProxy(AppDomain containingDomain)
{
_remoteProvider = (CrossDomainParameterProvider)containingDomain.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().FullName,
typeof(CrossDomainParameterProvider).FullName);
}
public object GetValue(string name)
{
return _remoteProvider.GetValue(name);
}
}
關於壽命的注釋
管理靜態類的重構的主要問題之一通常不是更改客戶端代碼(因為許多重構工具都支持這種更改,並且有一些技術可以安全地完成它),而是管理該類的生命周期。賓語。 實例對象依賴於活動的引用(否則它們是垃圾收集的),通常可以通過將它們放在某個地方的公共靜態成員中來使它們“易於訪問”,但是通常這是您首先要通過重構來避免的事情。
似乎不必擔心此問題,因為您將調用代碼附加在靜態類上,因此壽命將保持不變。
為每個靜態方法創建一個實例。 添加一個靜態單例變量,您可以將其分配給任何實現。 使靜態方法在靜態單例上調用實例方法。
這將允許您在運行時交換實現,但是您只能同時掛接一個實現。
現有代碼無需更改。
靜態類可以轉換為Singleton對象。
Singleton Objects支持接口。
接口可以用於不同的實現。
(1)問題的定義。
假設您有一個具有靜態成員的類。
-
StringsClass.cs
-
namespace Libraries
{
public static class StringsClass
{
public static string UppercaseCopy(string Value)
{
string Result = "";
// code where "Value" is converted to uppercase,
// and output stored in "Result"
return Result;
} // string UppercaseCopy(...)
public static string LowercaseCopy(string Value)
{
string Result = "";
// code where "Value" is converted to lowercase,
// and output stored in "Result"
return Result;
} // string LowercaseCopy(...)
public static string ReverseCopy(string Value)
{
string Result = "";
// code where "Value" is reversed,
// and output stored in "Result"
return Result;
} // string ReverseCopy(...)
} // class StringsClass
} // namespace Libraries
-
並且,使用該類中的靜態元素的一些代碼。
-
StringsLibraryUser.cs
-
using Libraries;
namespace MyApp
{
public class AnyClass
{
public void AnyMethod()
{
string Example = "HELLO EARTH";
string AnotherExample = StringsClass.LowercaseCopy(Example);
} // void AnyMethod(...)
} // class AnyClass
} // namespace MyApp
-
(2)首先將類轉換為非靜態類。
-
StringsClass.cs
-
namespace Libraries
{
public class StringsClass
{
public string UppercaseCopy(string Value)
{
string Result = "";
// code where "Value" is converted to uppercase,
// and output stored in "Result"
return Result;
} // string UppercaseCopy(...)
public string LowercaseCopy(string Value)
{
string Result = "";
// code where "Value" is converted to lowercase,
// and output stored in "Result"
return Result;
} // string LowercaseCopy(...)
public string ReverseCopy(string Value)
{
string Result = "";
// code where "Value" is reversed,
// and output stored in "Result"
return Result;
} // string ReverseCopy(...)
} // class StringsClass
} // namespace Libraries
-
(3)添加代碼允許類處理單個對象。
-
StringsClass.cs
-
namespace Libraries
{
public class StringsClass
{
private static Singleton instance = null;
private Singleton()
{
// ...
}
public static synchronized Singleton getInstance()
{
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public string UppercaseCopy(string Value)
{
string Result = "";
// code where "Value" is converted to uppercase,
// and output stored in "Result"
return Result;
} // string UppercaseCopy(...)
public string LowercaseCopy(string Value)
{
string Result = "";
// code where "Value" is converted to lowercase,
// and output stored in "Result"
return Result;
} // string LowercaseCopy(...)
public string ReverseCopy(string Value)
{
string Result = "";
// code where "Value" is reversed,
// and output stored in "Result"
return Result;
} // string ReverseCopy(...)
} // class StringsClass
} // namespace Libraries
-
(4)調用該類的代碼,應添加單例的引用。
-
StringsLibraryUser.cs
-
using Libraries;
namespace MyApp
{
public class AnyClass
{
public void AnyMethod()
{
string Example = "HELLO EARTH";
string AnotherExample = StringsClass.getInstance().LowercaseCopy(Example);
} // void AnyMethod(...)
} // class AnyClass
} // namespace MyApp
-
(5)定義一個接口,使用與先前的靜態類相似的聲明,並允許單例實現該接口。 在接口聲明中省略單例成員
-
StringsClass.cs
-
namespace Libraries
{
public interface StringsInterface
{
string UppercaseCopy(string Value);
string LowercaseCopy(string Value);
string ReverseCopy(string Value);
} // interface StringsInterface
public class StringsClass: StringsInterface
{
private static Singleton instance = null;
private Singleton()
{
// ...
}
public static synchronized Singleton getInstance()
{
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public string UppercaseCopy(string Value)
{
string Result = "";
// code where "Value" is converted to uppercase,
// and output stored in "Result"
return Result;
} // string UppercaseCopy(...)
public string LowercaseCopy(string Value)
{
string Result = "";
// code where "Value" is converted to lowercase,
// and output stored in "Result"
return Result;
} // string LowercaseCopy(...)
public string ReverseCopy(string Value)
{
string Result = "";
// code where "Value" is reversed,
// and output stored in "Result"
return Result;
} // string ReverseCopy(...)
} // class StringsClass
} // namespace Libraries
-
(6)在代碼中,如果您使用的是單例(包含靜態方法的上一類),請替換接口的單例。
-
StringsLibraryUser.cs
-
using Libraries;
namespace MyApp
{
public class AnyClass
{
public StringsInterface StringsHelper = StringsClass.getInstance().LowercaseCopy(Example);
public void AnyMethod()
{
string Example = "HELLO EARTH";
string AnotherExample = StringsHelper;
} // void AnyMethod(...)
} // class AnyClass
} // namespace MyApp
-
現在,您可以添加其他支持相同聲明和不同實現的類。
干杯。
-
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.