1. Dotnet9首页
  2. .NET
  3. ASP.NET Core

5min+:设计模式的迷惑?Provider vs Factory

NCC 的 5min+ 栏目由句幽和痴者工良等作者联合推出,是一个利用您的碎片化时间来学习和丰富计算机专业知识与 .NET 知识的博文系列,它所包含 .NET 体系中可能会涉及到的方方面面,如 C# 的小细节、ASP.NET Core、微服务等。

一说起设计模式,大家应该都不会太陌生。毕竟在面向对象的世界中,我们需要用到各种奇技淫巧的手段来构建我们的应用,而设计模式就是这些技巧的根本。如果您曾参与过计算机职业资格考试(俗称软考),就会发现:无论是初级、中级还是高级,都会有关于设计模式的考题,而且分值比重还很大。这也侧面说明了,学习设计模式的重要性。

如果一谈起设计模式,立马浮现在您脑中的模式有哪些呢:“单例”、“迭代器”、“外观”、“桥接”…… 然而,就在上周的时候,我突然遇到了这么一个问题:当我想为创建一个对象进行抽象时,我不知道如何命名该创建类的名称了。您可能会说“这还不简单,要创建肯定是属于创建类型,那很大几率就是 “XXFactory” 呀,就是所谓的工厂模式”。是的,我最初的时候也是这么想的,但是我心里又出现了另外的一个单词:“Provider”。

Provider?可能有些小伙伴会有一些陌生,因为它并没有出现在 GOF 所提出的 24 个模式中。而它又是什么东西呢?经过我一番查找,发现它是由微软在 .NET 1.1 Framework 提出的一种模式。当然,距离 .NET 1.1 Framework 发布至今已经过了很多年了。也正是经历了这么多年的成长,所以微软的许多代码中您都会发现 “Provider” 的身影。比如咱们在 ASP.NET Core 中再熟悉不过的 Logger,它就是由 “ILoggerProvider” 来创建的,还有依赖注入的 “IServiceProvider” 等等。

疑惑

可能也是因为看多了 “Provider” 这个单词,所以才出现了我上面的纠结。但是,我突然想了想,既然都是向外界提供一个结果,那么 Provider 和 Factory 到底有什么不同呢?

于是乎,我再次尝试了 “百度不行就谷歌” 的程序员大法进行一波骚操作。但是看了结果之后我的心是拔凉拔凉啊:😭

min+:设计模式的迷惑?Provider

好吧,这是在逼我下毒手啊!!如是乎,我决定自己来好好分析它。

注:后文的内容都将以分析 ILoggerProvider 来作为切入点。

当然,在进行了一圈疯狂搜索之后,也不是没有收获的。在必应中我查询到了这样一篇文章:The .NET 2.0 Framework Provider Pattern,该文章中里面提到了这样的一段话:

min+:设计模式的迷惑?Provider

它的意思是:Provider 模式是 策略模式 于 抽象工厂模式 的融合。

所以在这之前我们先来过一过 策略模式 于 抽象工厂模式吧,放心,时间不会太长。希望能在其中找到什么奥秘:

策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式(注意,这一点很重要)。

这里我就直接引用runoob上面的图片了,更多的模式说明您也可以跳转至对应链接。https://www.runoob.com/design-pattern/strategy-pattern.html

min+:设计模式的迷惑?Provider

策略模式的几个优点:1、算法可以自由切换。2、避免使用多重条件判断。3、扩展性良好。

所以总结一下,策略模式其实提供了一个可拔插的替换方案。

抽象工厂

接下来,咱们再来过一遍抽象工厂:抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种类型的设计模式属于创建型模式(注意,这一点很重要)。

min+:设计模式的迷惑?Provider

可能这个图看上去有一点点绕哈。说白了就是为不同创建的结果都提供一个工厂。

所以它具有这样的优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

总结就是:它为客户端提供了一种开箱即用的功能,客户端只管向工厂索取就可以得到结果了。

关于 Provider

好了,回到主题,那么关于 Provider 呢?它到底是什么样子呢?这里来看一看 .NET Core Logger 的相关接口:

public interface ILoggerProvider : IDisposable
{
    ILogger CreateLogger(string categoryName);
}
public interface ILoggerFactory : IDisposable
{
    ILogger CreateLogger(string categoryName);

    void AddProvider(ILoggerProvider provider);
}

是的,不管是 Provider 和 Factory 都具有一个叫做 CreateLogger 的方法。也就是说,他们都具有创建 Logger 的能力。那么我到底应该什么时候才使用 Provider,什么时候才使用 Factory 呢?这也是我最初十分纠结的原因。

但是从 ILoggerFactory 的另外一个方法,也许能看出端倪:AddProvider。这就证明了,可以往工厂里面添加 Provider。也就是说工厂里面可能存在着许许多多的提供程序。而这些提供程序可能都将是最后工厂创建出结果的必要支撑。

还记得上面咱们说的那两个模式的特点吗?策略模式是为了可拔插可替换方案,而 factory 是为了屏蔽细节的创建。假如 Provider 是结合了这两者的话,也就是说它可能会具有这两者的全部优点:可拔插 + 创建。

也就是说我们可以在项目中根据已有的 provider 接口,演变出各种的策略来,比如 XMLLogProvider、ConsoleLogProvider、JsonLogProvider 等等。

但是它仅仅关注的是它要创建的细粒度对象,而不像工厂一样负责各个粒度对象的拼装,最终产生一个大粒度对象。

那么我们能将各种 Provider 再提供给工厂,然后再让它来负责最终大对象的创建吗?。是的,ILoggerFactory 就是干了这样的事情。有兴趣的同学可以查看它的实现代码。

https://github.com/dotnet/extensions/blob/d656c4f7e22d1c0b84cab1b453c50ce73c89a071/src/Logging/Logging/src/LoggerFactory.cs

一个小故事

有一个名人叫做 Bob,他平时被各种商业会谈所占据了大部分的时间。所以他很聪明,将一些繁琐的事情都交给了他的管家。

这不,明天 Bob 就要参加一个晚会,但是高端的晚会是需要配上高端的礼服的。Bob 肯定没有心思去打理这些事情,所以他就把这个事情交给了他的管家。

管家收到了命令之后,立即做出了反应。他知道他得在今天为 Bob 采购一套完美的礼服,包括礼帽,燕尾服等。但是管家一想,我又不会做衣服啊。所以他电话联系了周围所有的服装厂商来和他们洽谈。哪个厂商提供怎样的衣服他都安排了下去,到最后各个厂商把衣服做好了之后都给了管家,并得到了自己应有的报酬。

最后,管家在采购的众多衣服中为 Bob 搭配了一个最好看的礼服。

在上面这个故事中,您可以把 Bob 理解为咱们的客户端,管家理解为工厂,服装供应商理解为 Provider。客户端只需要让工厂创建需要的东西就行了,它并不想知道这个东西是怎么来的。就好比 Bob 哪儿有那么多时间来关心衣服怎么来的一样。而工厂去找提供程序获取所需要的东西。就好比管家去找服装厂商。

这么一看,Provider 确实是在干它自己分内的事情,它只负责小颗粒对象的创建。就好比服装厂商,我只造帽子就只造帽子。而且它是可以随时替换的,就相当于上文管家可以联系其它的厂商一样,只要附近有厂商就可以了。

总结

那么用了这样的模式好处到底在哪儿呢?就像上面的故事来说,Bob 需要礼服的时候他只需要安排管家就可以了,管家离职了可以再换一个。而管家做礼服的时候,可以有许许多多的方案来备选,只要周围有服装厂商就可以了,方案不好就换一个厂商就行了。所以对于客户端来说,代码一直都没有变过(只需要叫工厂拿),而工厂又去找提供商。

所以,您会发现,咱们的代码同样是用的 Logger,但是用了不同的日志框架(比如 serilog)之后,日志显示的结果和存放的方式就可能不一样了。因为日志框架的底部实现了对应的日志提供代码。

总结一下 Provider:当我们需要创建一个小粒度对象的时候并且该对象未来可能会有多种创建方案的时候可以考虑使用 Provider。

那么 Provider 到底是属于 创建型模型还是行为型模式呢?好吧,我也不知道。个人感觉偏前一种更多一些吧。

本文内容仅供参考,因为该模式我并没有查找到官方的解释,大多数内容都是个人的理解。如有不足,还望各位大佬不吝赐教。😀

min+:设计模式的迷惑?Provider
min+:设计模式的迷惑?Provider

原文出处:微信公众号【句幽 NCC开源社区】

原文链接:https://mp.weixin.qq.com/s/tJGEhSfSoP9Q2dkfOFAn2A

本文观点不代表Dotnet9立场,转载请联系原作者。

发表评论

登录后才能评论