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

时间:2015-01-06 14:51:54

标签: c# architecture factory

我有一个引擎类,一个使用该引擎的 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实现)。

2 个答案:

答案 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");
    }
}