我有一个引擎类,一个使用该引擎的 App 类,以及一个抽象出来的 IEngine 接口:
public interface IEngine
{
void Foo();
}
public class Engine1 : IEngine
{
public void Foo()
{
//...
}
}
public class App1
{
public void Do()
{
IEngine1 e = new Engine1();
e.Foo();
}
}
由于我可以有多个引擎,我正在实现一个生产引擎的引擎工厂,并将它们作为IEngine返回:
public class EngineFactory
{
public IEngine CreateEngine(string engineName)
{
//returns the right engine according to 'engineName'
}
}
这样,App和引擎松散耦合,任何 App 类都不会引用具体的引擎。
最后,我希望能够为(所有)我的引擎添加新方法。很自然的事情是将这些方法添加到 IEngine ,但如果我这样做,我将不得不重新编译所有使用 IEngine 的应用程序。我的解决方案是创建一个新界面 IEngine2 :
public interface IEngine2 : IEngine
{
void Goo();
}
但这会迫使我改变工厂中的 CreateEngine 方法签名。
我该如何避免?我应该将工厂改为通用吗?我应该使用DI吗?还有别的吗?
编辑:您可以这样思考 - 我负责引擎实施,我的客户正在实施应用。现在有一个客户需要所有引擎的新功能,我需要实现该功能,而不必强迫所有其他客户重新编译他们的应用程序(类似于API实现)。
答案 0 :(得分:4)
解决方案可能是使用能力模式:
public interface IEngine
{
bool TryGetCapability<T>(out T capability);
}
public interface ICapability1
{
void Foo();
}
public class Engine1 : IEngine, ICapability1
{
public bool TryGetCapability<T>(out T capability)
{
if (this is T)
{
capability = this as T;
return true;
}
capability = default(T);
return false;
}
public void Foo()
{
//...
}
}
public class App1
{
public void Do()
{
IEngine e = new Engine1();
ICapability1 cap1;
if (e.TryGetCapability(out cap1))
{
cap1.Foo();
}
}
}
我使用引擎类来实现该功能,但在真实的应用程序中,它通常会在每个功能的一个类上实现。
答案 1 :(得分:3)
这是COM世界中的常见问题,一旦发布,就不应修改界面。 COM通常使用的方式是使用继承自先前版本的新版本编号接口。
public interface IEngine
{
void Foo();
}
public interface IEngine2 : IEngine
{
void Goo();
}
public class EngineA : IEngine
{
public void Foo()
{
//...
}
}
public class EngineB : IEngine2
{
public void Foo()
{
//...
}
public void Goo()
{
//...
}
}
对于您的工厂,您可以让它始终返回IEngine或使其成为通用的,用户必须指定项目必须支持的最小接口。
public class EngineFactory
{
public T CreateEngine<T>(string engineName) where T : IEngine
{
//returns the right engine according to 'engineName'
}
}
public class App
{
public void Do(EngineFactory factory)
{
//Creates a instance of EngineA
IEngine e = factory.CreateEngine<IEngine>("EngineA");
//returns a instance of EngineB
IEngine eB = factory.CreateEngine<IEngine>("EngineB");
//returns null
IEngine2 e2 = factory.CreateEngine<IEngine2>("EngineA");
//returns a instance of EngineB
IEngine2 e2B = factory.CreateEngine<IEngine2>("EngineB");
}
}