可以让接口依赖于 class 吗?

Can you have an interface be dependent on a class?

我正在研究 SOLID 原则,对与接口相关的依赖管理有疑问。

我正在阅读的书(Adaptive Code via C# by Gary McLean Hall)中的一个示例显示了一个 TradeProcessor class 将获得交易数据,对其进行处理,并将其存储在数据库中。贸易数据由名为 TradeRecord 的 class 建模。 TradeParser class 将处理将收到的交易数据转换为 TradeRecord 个实例。 TradeProcessor class 仅引用 ITradeParser 接口,因此它不依赖于 TradeParser 实现。

作者有 Parse 方法(在 ITradeParser 接口中) return 一个 IEnumerable<TradeRecord> 集合,其中包含已处理的贸易数据。这是否意味着 ITradeParser 现在依赖于 TradeRecord class?

作者不应该做一些事情,比如制作一个 ITradeRecord 界面并拥有 Parse return 一个 ITradeRecord 实例的集合吗?还是我遗漏了什么重要的东西?

代码如下(TradeRecord的实现无关,省略):

TradeProcessor.cs

public class TradeProcessor
{
    private readonly ITradeParser tradeParser;

    public TradeProcessor(ITradeParser tradeParser)
    {
        this.tradeParser = tradeParser;
    }

    public void ProcessTrades()
    {
        IEnumerable<string> tradeData = "Simulated trade data..."
        var trades = tradeParser.Parse(tradeData);

        // Do something with the parsed data...
    }
}

ITradeParser.cs

public interface ITradeParser
{
    IEnumerable<TradeRecord> Parse(IEnumerable<string> tradeData);
}

这是一个很好的问题,涉及纯度和实用性之间的权衡。

是的,按照纯粹的原则,您可以说 ITradeParser.Parse 应该 return ITraceRecord 接口的集合。毕竟,为什么要把自己束缚在特定的实现上?

但是,您可以更进一步。你应该接受 IEnumerable<string> 吗?或者你应该有某种ITextContainerI32bitNumeric 而不是 int?当然,这是 reductio ad absurdum,但它表明我们总是会在某个时候到达我们正在做的事情 something ,具体对象(数字、字符串、TraceRecord 等),而不是抽象。

这也引出了我们为什么首先使用接口的要点,即定义逻辑和功能的契约。 ITradeProcessor 是一个未知实现的合约,可以被替换或更新。 TradeRecord 不是实施合同,它 实施。如果它是一个 DTO 对象,它似乎是,那么接口和实现之间就没有区别,这意味着定义这个契约没有真正的目的——它隐含在具体的 class.[=18= 中]

The author has the Parse method (in the ITradeParser interface) return an IEnumerable collection that holds the processed trade data.

Doesn't that mean that ITradeParser is now dependent on the TradeRecord class?

是的,ITradeParser 现在与 TradeRecord 紧密耦合。鉴于这个问题更学术的方法,我可以看到你来自哪里。但是 TradeRecord 是什么?根据定义,记录通常是简单的非智能数据(有时称为 POCO、DTO 或模型)。

在某些时候,抽象的潜在收益不如它造成的复杂性有价值。这种方法在实践中很常见——模型(如我所称)是流经应用程序层的密封类型。作用于模型的层被抽象为接口,因此可以单独模拟和测试每一层。

例如,客户端应用程序可能具有视图、视图模型和存储库层。每一层都知道如何使用具体的记录类型。但是 ViewModel 可以连接起来与模拟的 IRepository 一起工作,它使用硬编码的模拟数据构建具体类型。此时抽象的 IModel 没有任何好处 - 它只有直接数据。