You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请教特定场景下的依赖倒置原则(Dependency Inversion Principle)

搞懂依赖倒置原则(DIP):以HTMLAgilityPack为例

嘿,我来帮你把这个事儿掰明白——你之前对DIP的理解确实跑偏了,咱们先把核心概念拉回来,再结合你提到的HTMLAgilityPack例子说清楚怎么落地。

首先,依赖倒置原则的核心是两句话:

  • 高层模块(你的业务逻辑)不依赖低层模块(比如HTMLAgilityPack这种工具库),两者都依赖抽象
  • 抽象不依赖细节,细节依赖抽象

你之前想的“中间类返回默认值”其实是容错处理,和DIP的核心思想不沾边。而别人提到的HTMLAgilityPack的使用场景,恰恰是DIP要解决的问题:如果你的业务逻辑直接写using HtmlAgilityPack;并调用它的API,那你的业务代码就和这个具体的库死死绑定了——以后要换解析库、要做单元测试都会非常麻烦。

那怎么让业务逻辑不依赖这类关键组件?咱们一步步来:

第一步:把依赖的功能抽象成接口

先想清楚你用HTMLAgilityPack是要做什么——比如解析HTML文本、提取链接?那咱们就把这些功能定义成一个抽象接口,完全不涉及具体库:

public interface IHtmlParser
{
    // 提取HTML中的纯文本
    string ExtractPlainText(string htmlContent);
    // 提取HTML中的所有链接
    List<string> ExtractAllLinks(string htmlContent);
}

第二步:让业务逻辑依赖这个抽象

你的业务服务类只需要依赖刚才的IHtmlParser接口,根本不知道HTMLAgilityPack的存在:

public class ArticleProcessingService
{
    private readonly IHtmlParser _htmlParser;

    // 通过构造函数注入抽象(这就是依赖注入的基础)
    public ArticleProcessingService(IHtmlParser htmlParser)
    {
        _htmlParser = htmlParser;
    }

    // 业务逻辑:处理文章HTML,返回干净的文本内容
    public string ProcessArticleHtml(string rawArticleHtml)
    {
        // 只调用抽象接口的方法,完全不关心底层用什么工具实现
        var plainText = _htmlParser.ExtractPlainText(rawArticleHtml);
        // 这里可以加你的业务逻辑,比如过滤敏感词、格式化文本等
        return plainText.Replace("敏感词", "***").Trim();
    }
}

第三步:写一个基于HTMLAgilityPack的具体实现

现在才轮到HTMLAgilityPack出场——写一个类实现刚才的IHtmlParser接口,把具体的解析逻辑放在这里:

using HtmlAgilityPack;

public class HtmlAgilityPackParser : IHtmlParser
{
    public string ExtractPlainText(string htmlContent)
    {
        var doc = new HtmlDocument();
        doc.LoadHtml(htmlContent);
        return doc.DocumentNode.InnerText.Trim();
    }

    public List<string> ExtractAllLinks(string htmlContent)
    {
        var doc = new HtmlDocument();
        doc.LoadHtml(htmlContent);
        var linkNodes = doc.DocumentNode.SelectNodes("//a[@href]");
        
        return linkNodes?.Select(node => node.GetAttributeValue("href", string.Empty))
                         .ToList() ?? new List<string>();
    }
}

第四步:把具体实现注入到业务逻辑中

最后在程序启动的地方(比如ASP.NET Core的Startup.cs,或者控制台的Main方法),把具体的解析器实例注入到业务服务里:

// 手动实例化的例子(如果用DI容器会更优雅,比如ASP.NET Core的services.AddScoped<IHtmlParser, HtmlAgilityPackParser>();)
var htmlParser = new HtmlAgilityPackParser();
var articleService = new ArticleProcessingService(htmlParser);

// 调用业务逻辑,完全和HTMLAgilityPack解耦
var cleanedArticle = articleService.ProcessArticleHtml("<html><body>...</body></html>");

这么做的好处是什么?

  • 解耦:以后如果觉得HTMLAgilityPack不好用,想换成AngleSharp或者其他解析库,只需要写一个新的AngleSharpParser : IHtmlParser,业务逻辑一行都不用改
  • 易测试:单元测试的时候,你可以写一个Mock的IHtmlParser(比如返回固定文本),不用真的加载HTML文件或者调用第三方库,测试速度快且稳定
  • 灵活扩展:比如你想加容错逻辑,写一个FallbackHtmlParser : IHtmlParser,当主解析器出错时返回默认值,业务逻辑依然不用改,只需要替换注入的实例就行

内容的提问来源于stack exchange,提问作者sublimeaces

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:59:34