创建一个支持随时间变化的工厂

本文关键字:时间 变化 工厂 支持 一个 创建 | 更新日期: 2023-09-27 18:36:38

我有一个引擎类,一个使用该引擎的应用程序类,以及一个抽象的接口 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 实现)。

创建一个支持随时间变化的工厂

解决方案可能是使用功能模式:

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

我使用引擎类来实现该功能,但在实际应用程序中,它通常会在每个功能的一个类上实现。

这是 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");
    }
}