请教特定场景下的依赖倒置原则(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
相关产品推荐
相关产品推荐

