【问题标题】:Simulate Inheritance by Extension通过扩展模拟继承
【发布时间】:2015-02-27 17:17:51
【问题描述】:

我有一个类库,用于描述连接硬件的不同部分,例如钉子、螺钉和螺栓,我们将其称为 ConnectorLibrary。我正在尝试在该库之上构建一个库,用于分析该库中每个类的抓握能力,我们将其称为 ConnectorGripAnalysisLibrary。

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

对于我需要实现的基础库中的每个类,有一种不同的方法来确定抓地力。所以对于BoltScrew,我需要实现一个方法,比如DoesPassGripTest(Board board)。 (Board 只是一个示例参数)

如果我要在连接器库中实现它,我会将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");
    }
}

我想在 ConnectorGripAnalysisLibrary 中保持“开闭原则”,以便在将新的 Connector 添加到 ConnectorLibary 的情况下,除了添加新类之外不需要修改 ConnectorGripAnalysisLibrary。 "开放扩展,关闭修改"

但是我如何才能将此功能构建到构建在 ConnectorLibrary 之上的 GripAnalysisLibrary 中。有没有一种巧妙的方法可以做到这一点?

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

【问题讨论】:

  • 接口怎么样,所以每个类型都实现DoesPassGripTest 做任何它需要的事情?
  • @Plutonix 如何在不派生新类型的情况下通过扩展实现接口?
  • 为什么它需要成为一个扩展?
  • 因为我不希望 ConnectorLibrary 包含 GripAnalysis 代码和功能。 ConnectorLibrary 将是开源的,而 ConnectorGripAnalysisLibrary 将是专有的
  • 您将无法向Connector 添加虚拟/抽象方法。我认为您所希望的最好的结果是某种从Connector 类型到ConnectorTester 类型的映射,它实现了测试逻辑。如果需要,您可以将所有内容包装在扩展方法中。

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


【解决方案1】:

要将此作为扩展方法,您需要创建一个类来定义 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;
    }
}

创建一个存储库,该存储库可用于使用反射按类型获取 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;
    }
}

连接器的扩展利用存储库为给定的连接器创建适当的 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 中添加对连接器的支持现在只需添加一个继承自具有具体连接器类型的 ConnectorGripAnalyzer 的类。只需为特定的连接器适当地覆盖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;
    }
}

【讨论】:

  • 我理解这个解决方案,但我不想使用它,因为它违反了开闭原则,并且每次将新的Connector 添加到 ConnectorLibrary 时都需要更改。
  • 我同意你所说的。我只是提供了一个扩展方法的实现,它将与您要使用的客户端代码一起使用。您的连接器是否提供任何其他识别信息作为其公共接口的一部分?
猜你喜欢
  • 2010-11-07
  • 2017-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多