通过扩展模拟继承

时间:2014-12-31 17:08:08

标签: c# inheritance polymorphism extension-methods solid-principles

我有一个类库,描述了不同的连接硬件,如钉子,螺钉和螺栓,我们称之为ConnectorLibrary。我试图在那个库之上构建一个库,它将处理分析该库中每个类的抓握能力,我们称之为ConnectorGripAnalysisLibrary。

对于此问题,我们将使用这些类:ScrewBoltConnectorScrewBolt都继承自Connector(这是一个抽象类),它们都在ConnectorLibrary中实现。

我需要一种不同的方法来确定我需要实现的基础库中每个类的抓握。因此,对于BoltScrew,我需要实现DoesPassGripTest(Board board)等方法。 (董事会只是一个示例参数)

如果我要在Connectorlibrary中实现它,我会将DoesPassGripTest放入Connector抽象方法中,并在各自的派生类中实现不同的公式。

目标是能够让我的代码在ConnectorGripAnalysisLibrary中像这样工作:

[Test()]
public static void CheckScrewAndBoltGripTest()
{
    Board board = new Board();

    Bolt b = new Bolt();
    Screw s = new Screw();
    List<Connector> connectors = new List<Connector>()

    connectors.add(b);
    connectors.add(s);

    foreach(var connector in connectors)
    {
        if(!connector.DoesPassGripTest(board));
            throw new Exception("Grip Test Fails");
    }
}

我想保持&#34;开放封闭原则&#34;在ConnectorGripAnalysisLibrary中,如果将新连接器添加到ConnectorLibary,则除了添加新类之外,不需要修改ConnectorGripAnalysisLibrary。 &#34;为扩展而开放,为修改而关闭&#34;

但是如何将这个功能构建到构建于ConnectorLibrary之上的GripAnalysisLibrary中。我能做到这一点吗?

我不希望ConnectorLibrary包含GripAnalysis代码和功能。 ConnectorLibrary是开源的,而GripAnalysisLibrary是专有的。

1 个答案:

答案 0 :(得分:0)

要将此作为扩展方法执行此操作,您需要创建一个类来定义 Connector 的扩展,并将类包含在您希望在Connector实例上使用DoesPassGripTest方法的任何位置。扩展的基本概要是:

public static class ConnectorExtensions
{
    public static bool DoesPassGripTest(this Connector connector, Board board)
    {
        // Some logic to determine which connector is being used
    }
} 

我不知道是否有任何其他方法可以确定哪个连接器正在传递,而不是类型检查,因为您没有提供有关连接器或夹点分析逻辑的任何详细信息。您的扩展需要知道如何评估给定每个连接器类型的给定板的测试。

编辑:

基于您希望ConnectorGripAnalysisLibrary可以轻松扩展且无需修改,这里是使用反射的方法的伪代码,允许您只需为每个新连接器添加一个类。

创建一个界面,提供运行分析的信息以及它所用的连接器类型:

public interface IConnectorGripAnalyzer
{
    Type ConnectorType { get; }
    bool DoesPassGripTest(Board board);
}

使用泛型创建基类,以便轻松实现具体类:

public class ConnectorGripAnalyzer<T> : IConnectorGripAnalyzer where T : Connector
{
    public Type ConnectorType 
    { 
        get { return typeof(T); }
    }

    public virtual bool DoesPassGripTest(Board board)
    {
        return true;
    }
}

创建一个可用于通过Type使用反射获取IConnectorGripAnalyzer实例的存储库。首次使用时,它会收集所有各种分析仪并按连接器类型存储它们:

public static class ConnectorAnalyzerRepository
{
    private Dictionary<Type, IConnectorGripAnalyzer> connectorGripAnalyzers;

    public IConnectorGripAnalyzer GetGripAnalyzer(Connector connector)
    {
        if (connectorGripAnalyzers == null)
        {
            connectorGripAnalyzers = new Dictionary<Type, IConnectorGripAnalyzer>();

            var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(IConnectorGripAnalyzer).IsAssignableFrom(t));
            foreach (var t in types)
            {
                var c = Activator.CreateInstance(t) as IConnectorGripAnalyzer;
                if (c == null)
                    continue;

                connectorGripAnalyzers[c.ConnectorType] = c;
            }
        }

        return connectorGripAnalyzers.ContainsKey(typeof(connector)) ? connectorGripAnalyzers[typeof(connector)] : null;
    }
}

Connector的扩展利用存储库为给定的Connector创建适当的IConnectorGripAnalyzer实例。如果连接器的类型没有实现,则该示例将引发异常,但您可以返回false或将其记录为配置问题:

public static class ConnectorExtensions
{
    public static bool DoesPassGripTest(this Connector connector, Board board)
    {
        var analyzer = ConnectorAnalyzerRepository.GetGripAnalyzer(connector);
        if (analyzer == null)
            throw new ArgumentException("Invalid connector type"); // Do whatever you want with the failure

        return analyzer.DoesPassGripTest(board);
    }
}

在ConnectorGripAnalysisLibrary中添加对Connector的支持现在需要添加一个继承自ConnectorGripAnalyzer的类和具体的Connector类型。所需的只是适当覆盖特定连接器的DoesPassGripTest(Board board)

public class NailConnectorGripAnalyzer : ConnectorGripAnalyzer<NailConnector>
{
    public override bool DoesPassGripTest(Board board)
    {
        return true;
    }
}

public class ScrewConnectorGripAnalyzer : ConnectorGripAnalyzer<ScrewConnector>
{
    public override bool DoesPassGripTest(Board board)
    {
        return true;
    }
}