昨天,我们团队的两个家伙来找我,告诉我一个不常见的问题。我们在一个winforms应用程序中使用了第三方组件。所有的代码都是针对它编写的。然后,他们希望将同一供应商的另一个第三方组件合并到我们的应用程序中。令他们高兴的是,他们发现第二个组件与第一个组件具有完全相同的公共成员。但令他们沮丧的是,这两个组件具有完全独立的继承层次结构,并且没有实现公共接口。让你想知道..。这让我很好奇。
这个问题的一个例子:
Incompatible Types http://www.freeimagehosting.net/uploads/f9f6b862f1.png
public class ThirdPartyClass1
{
public string Name
{
get
{
return "ThirdPartyClass1";
}
}
public void DoThirdPartyStuff ()
{
Console.WriteLine ("ThirdPartyClass1 is doing its thing.");
}
}
public class ThirdPartyClass2
{
public string Name
{
get
{
return "ThirdPartyClass2";
}
}
public void DoThirdPartyStuff ()
{
Console.WriteLine ("ThirdPartyClass2 is doing its thing.");
}
}
令人高兴的是,他们觉得复制和粘贴他们为第一个组件编写的代码并不是正确的答案。因此,他们正在考虑将组件即时赋值到对象引用中,然后在检查其类型后修改代码以进行条件强制转换。但这可以说比复制和粘贴方法更丑陋。
所以他们问我是否可以写一些反射代码来访问属性并调用两种不同的对象类型的方法,因为我们知道它们是什么,而且它们是完全相同的。但我的第一个想法是优雅消失了。我认为必须有一个更好、更优雅的解决方案来解决这个问题。
发布于 2010-03-19 08:41:36
我的第一个问题是,这两个第三方组件类是密封的吗?他们不是。至少我们有这一点。
因此,由于它们不是密封的,所以问题可以通过以下方式解决:
从两个第三方类的重合成员中提取一个公共接口。我叫它Icommon。
public interface ICommon
{
string Name
{
get;
}
void DoThirdPartyStuff ();
}
然后创建两个新类:分别从ThirdPartyClass1和ThirdPartyClass2继承的DerivedClass1和DerivedClass2。这两个新类都实现了ICommon接口,但在其他方面都是完全空的。
public class DerivedClass1
: ThirdPartyClass1, ICommon
{
}
public class DerivedClass2
: ThirdPartyClass2, ICommon
{
}
现在,即使派生类是空的,接口仍由基类满足,这是我们首先从基类中提取接口的地方。生成的类图如下所示。
alt text http://www.freeimagehosting.net/uploads/988cadf318.png
因此,现在,我们不再像以前那样:
ThirdPartyClass1 c1 = new ThirdPartyClass1 ();
c1. DoThirdPartyStuff ();
我们现在可以这样做:
ICommon common = new DerivedClass1 ();
common. DoThirdPartyStuff ();
使用DerivedClass2也可以做到这一点。
其结果是,我们所有引用ThirdPartyClass1实例的现有代码都可以保持原样,只需将ThirdPartyClass1引用换成ICommon引用即可。然后,可以为ICommon引用提供DerivedClass1或DerivedClass2的实例,这两个实例分别从ThirdPartyClass1和ThirdPartyClass2继承。一切都很好用。
我不知道这是否有一个特定的名称,但对我来说,它看起来像是适配器模式的变体。
也许我们可以用C# 4.0中的动态类型来解决这个问题,但这不会带来编译时检查的好处。
我很想知道是否有人有解决这个问题的另一种优雅的方法。
发布于 2010-03-19 09:04:55
如果你使用的是.Net 4,你可以避免做很多这样的事情,因为动态类型可以帮助你做你想要的事情。但是,如果使用.Net 2+,还有另一种(不同的方式)来实现这一点:
您可以使用类似于Deft Flux的鸭子类型库来处理您的第三方类,就好像它们实现了一个接口一样。
例如:
public interface ICommonInterface
{
string Name { get; }
void DoThirdPartyStuff();
}
//...in your code:
ThirdPartyClass1 classWeWishHadInterface = new ThirdPartyClass1()
ICommonInterface classWrappedAsInterface = DuckTyping.Cast<ICommonInterface>(classWeWishHadInterface);
classWrappedAsInterface.DoThirdPartyStuff();
这就避免了必须为所有这些类手动构建派生包装类,并且只要类具有与接口相同的成员就可以工作
发布于 2010-03-19 08:40:42
一些包装纸怎么样?
public class ThirdPartyClass1 {
public string Name {
get {
return "ThirdPartyClass1";
}
}
public void DoThirdPartyStuff() {
Console.WriteLine("ThirdPartyClass1 is doing its thing.");
}
}
public interface IThirdPartyClassWrapper {
public string Name { get; }
public void DoThirdPartyStuff();
}
public class ThirdPartyClassWrapper1 : IThirdPartyClassWrapper {
ThirdPartyClass1 _thirdParty;
public string Name {
get { return _thirdParty.Name; }
}
public void DoThirdPartyStuff() {
_thirdParty.DoThirdPartyStuff();
}
}
对ThirdPartyClass2使用相同的包装器,然后在所有方法中使用...and接口。
https://stackoverflow.com/questions/2475920
复制相似问题