身边的设计模式(三):抽象工厂 与 依赖注入

上篇文章,我们说到了简单工厂和工厂方法,如果没看过的,请先看上篇,不然的话,可能有些吃力,或者直接点击阅读原文,查看我博客园的对应详细版的文章。

大家学到了这里,我建议自己可以练习练习,可以写一下部分代码,很好的区分下【简单工厂】和【工厂方法】之间的区别和练习,如果说你已经看懂了,或者说自己已经练习好了,那咱们就继续往下说今天的重要内容 —— 抽象工厂。

 

再说抽象工厂之前呢,咱们先简单总结一下:

1、我们知道,工厂模式属于创建型开发模式的一元,他的作用就是创建我们需要的对象,如果一个一个创建的话,会很麻烦,所以我们诞生出来了一个【简单工厂】,这个简单工厂只是简单的人为的把几个对象的实例给堆起来,通过type 来区分,然后分别 new 实例化,有时候也是一个很好的方案,但是这样有一个弊端,违背了我们开发六大原则中的——OCP开放关闭原则,所以这个时候,我们就又多出来一个新的概念【工厂方法】。

2、【工厂方法】是在【简单工厂】的基础上,做了相应的改良——通过多个子类的形式,来替换之前的 type 分类法,对内修改关闭,对外新增打开,这样无论是代码整洁上,还是扩展封装上,都有了很好的体验,同时也满足了我们的OCP开发原则。

3、但是!这种方案真的就很好了么,我们再来回忆一下,我们无论是简单工厂,还是工厂方法,都是生成的单独的一个类,好处是可以一一的慢慢扩展,但归根结底还是在处理一个类,我们平时开发的时候,处理一个类是很多,比如常见的helper,Log 之类的。但是这不是我们开发的重点,我们平时使用最多的还是 Service 类,或者 Repository 类,里边有很多,各种各样的的类,比如 User 表,Role 表,Permission 表等等,每一个实体又都对应各自的一个服务类或者仓储类。

4、那这个时候,我们使用上边的【工厂方法】还行么?肯定是不行的!因为我们上边是一个二维体系,EFCoreRepository 、SugarRepository、DapperRepository等等,是这样的二维,我们现在要做的就是在这个二维的基础上,再加上一个维度,就是要解决 User 、Role、Permission 实体等等这种一组或者一系列的类创建的问题。

5、那就是今天下边要说到的【抽象工厂】模式。

 

注意,下边的例子可能不太恰当,只是作为理解抽象工厂模式来使用,具体开发中,可能有出入。

这篇文章内容不是很全,我在博客园重新编排了一下,然后也同时加上了一个小故事,更方便理解,大家可以点击【阅读原文】来查看。

 

 一、抽象工厂模式

 

上边的问题我们都看到了,我们要解决一系列一组类创建的问题,引申出来了抽象工厂模式,那下边我们就简单写一些代码,看看是否跑的通。

1、创建一个核心层,添加多个仓储操作

我们毕竟要操作数据库嘛,所以肯定需要仓储来持久化,那我们就创建一个 FactoryPattern.Core 层,用来存放我们整个项目核心的底层。首先要创建的就是几个仓储:

/// <summary>/// 定义抽象的基类仓储/// </summary>public abstract class BaseRepository{    /// <summary>    /// 创建    /// </summary>    public abstract void Add();    /// <summary>    /// 删除    /// </summary>    public abstract void Delete();    /// <summary>    /// 修改    /// </summary>    public abstract void Update();    /// <summary>    /// 查询    /// </summary>/    public abstract void Query();}


/// <summary> /// 定义抽象用户仓储,继承抽象基类仓储 /// 抽象的目的,是为了给UserRepositoryEFCore、UserRepositorySugar、 /// 做父类 /// </summary> public abstract class UserRepository: BaseRepository { }

/// <summary> /// 同 UserRepository /// </summary> public abstract class RoleRepository: BaseRepository { }
/// <summary> /// 同 UserRepository /// </summary> public abstract class PermissionRepository: BaseRepository { }
 

 

那基本的仓储都已经定义好了,现在就需要一个工厂来生产这一系列产品了,所以我们定义一个抽象工厂类:

 /// <summary> /// 抽象工厂类,提供创建不同仓储接口 /// </summary> public abstract class AbstractFactory {     // 抽象工厂提供创建一系列产品的接口     public abstract UserRepository UserRepository();     public abstract RoleRepository RoleRepository();     public abstract PermissionRepository PermissionRepository(); }

 

结构如下:

 

2、创建EFCore仓储工厂层

说人话就是,刚刚我们不是定义了一个抽象的工厂么,用来生产我们数据库中一系列一组的产品,也就是数据库表,那现在我们就需要指定具体的工厂来生产他们了,首先第一个我们就用EFCore这个工厂来生产,创建一个 FactoryPattern.Repository.EFCore 类库,并引用 Core 核心层

 

 

 

首先呢,我们就要在这一层中,对那几个抽象的仓储类做重写,对应每一个EFCore 版本的仓储类,可能你会问为什么,要每一个重写下,还是OCP原则,而且还有一个愿意,Sqlsugar 可能某些表达式查询,在EFCore里不能用,所以必须每一个重写出来。

这里有一个地方就是,可以在EFCore也针对基类仓储做一个基类的,但是后来有类型不一致问题,大家可以自己看看.

 /// <summary> /// EFCore User 仓储,继承User仓储 /// </summary> public class UserRepositoryEFCore : UserRepository {     public override void Add()     {         throw new NotImplementedException();     }
public override void Delete() { throw new NotImplementedException(); }
public override void Query() { throw new NotImplementedException(); }
public override void Update() { throw new NotImplementedException(); } }

// 其他两个表也是这个情况,不粘贴代码了。

 

那现在有了子仓储产品了,我们就开始加工生产了,创建 EFCoreRepositoryFactory.cs ,并继承抽象工厂,同时实现抽象方法:

 /// <summary> /// EFCore 仓储子工厂 /// 用来生产各个实体仓储 /// </summary> public class EFCoreRepositoryFactory : AbstractFactory {     public override PermissionRepository PermissionRepository()     {         return new PermissionRepositoryEFCore();     }
public override RoleRepository RoleRepository() { return new RoleRepositoryEFCore(); }
public override UserRepository UserRepository() { return new UserRepositoryEFCore(); } }

 

 

结构如下:

 

3、同理,创建Sugar仓储工厂层

过程和上边的一模一样,我就不多说了,整体结构还是这样的:

 

 

 

 

4、控制器调用实例

我们在 api 层,引用刚刚创建的两个仓储层项目:

 

 

然后开始调用:

[HttpGet]public void Get(){    // 实例化工厂,这里用来生产 efcore 这一系列的 产品    AbstractFactory efcoreFactory = new EFCoreRepositoryFactory();    efcoreFactory.UserRepository().Add();    efcoreFactory.RoleRepository().Delete();    efcoreFactory.PermissionRepository().Query();

// 实例化工厂,这里用来生产 sugar 这一系列的 产品 AbstractFactory sugarFactory = new SugarRepositoryFactory(); sugarFactory.UserRepository().Add(); sugarFactory.RoleRepository().Delete(); sugarFactory.PermissionRepository().Query();}

 

结果我就不调试,肯定是没有问题的,毕竟仅仅是类的调用嘛,如果说你从上边往下看,看到了这里,还没有问题,并且能大概明白其中的意义,那你的工厂模式已经完全学会了,特别是这个抽象工厂,一直很绕,而且也使用的不是很直观,用途不是很多。

 

现在我们再简单的说明一下,我们通过【抽象工厂】模式,慢慢的明白了,其实抽象工厂是在【工厂方法】模式的基础上,往外又多做了一套封装,目的就是解决生产一系列产品的时候,工厂方法无法满足的问题。

所以这个时候,如果有人问你二者的区别,核心的区别就是:如果是一个产品用工厂方法,一系列产品,用抽象工厂

 

但是虽然解决了问题,还是有很多的问题的,单单从上边我们创建了那么多的子类,以及子类必须重写这一点来看,这么设计肯定有弊端,那有什么改进的呢?咱们继续往下看

 

 

 二、抽象工厂与依赖注入

这里我就不详细说了,其实就是一个思路的用法,这里举个例子就行了,大家肯定都用过三层架构,其中有一个数据访问层 DALFactory ,我们平时使用的时候,就是直接把类的实例给 return 出来,如果我们同时连接多个数据库呢?那这样的话,我们就像上边说到的,建立多个 DAL 层,比如 DALSqlServer、DALMysql 等等,那我们如何通过接口来获取服务呢,就是通过反射指定的程序集来实现,这个就是简单的使用了抽象工厂。

比如这个网上的图片,就是这个意思,大家看个意思就行:

 

 

说到这里大家有没有了解到一些小小的心得,似乎这个和有一个东西很像!对!就是我们平时使用的依赖注入。其实我们可以想一想,我们在服务注册的时候,通过反射将多个服务注册到容器里,然后我们再使用的时候,是容器通过接口别名,给我们找到指定的具体服务,甚至也实现了一个接口,多个服务的操作,这个就是工厂模式和依赖注入的小小的关系。

 

总结一下:

今天我们通过简单的代码,一步一步,从【简单工厂】开始,了解到了多个类是如何创建的,然后明白了【工厂方法】模式,更好的实现了OCP原则,也实现了封装多态的原理,接下来咱们通过【抽象工厂】的举例,进一步对一系列一组产品生产的时候,所采用的方案,到最后,我们简单的说明了一下反射以及依赖注入和工厂模式的关系,可能读起来还是有点儿凌乱,不过我还是简单大家多多的学学,查查资料,因为我认为,设计模式是结构的基础,而工厂模式又是设计模式的基础,可见其重要性,如果看不懂没关系,等我直播讲课吧。 

当然抽象工厂也是有一些弊端的,比如:

    3.1】、抽象工厂模式的优点:【抽象工厂】模式将系列产品的创建工作延迟到具体工厂的子类中,我们声明工厂类变量的时候是使用的抽象类型,同理,我们使用产品类型也是抽象类型,这样做就尽可能的可以减少客户端代码与具体产品类之间的依赖,从而降低了系统的耦合度。耦合度降低了,对于后期的维护和扩展就更有利,这也就是【抽象工厂】模式的优点所在。可能有人会说在Main方法里面(这里的代码就是客户端的使用方)还是会使用具体的工厂类,对的。这个其实我们通过Net的配置,把这部分移出去,最后把依赖关系放到配置文件中。如果有新的需求我们只需要修改配置文件,根本就不需要修改代码了,让客户代码更稳定。依赖关系肯定会存在,我们要做的就是降低依赖,想完全去除很难,也不现实。

   3.2】、抽象工厂模式的缺点:有优点肯定就有缺点,因为每种模式都有他的使用范围,或者说要解决的问题,不能解决的问题就是缺点了,其实也不能叫缺点了。【抽象工厂】模式很难支持增加新产品的变化,这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这样也就违背了“开发——封闭”原则。

   3.3】、抽象工厂模式的使用场景:   如果系统需要多套的代码解决方案,并且每套的代码方案中又有很多相互关联的产品类型,并且在系统中我们可以相互替换的使用一套产品的时候可以使用该模式,客户端不需要依赖具体实现。

 

 

 三、示例代码

https://github.com/anjoy8/DesignPattern/tree/master/FactoryPattern

 

【参考文献:】

1、Dependency Injection vs Factory Pattern

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java基础知识——Java集合详解

数组是Java很常见的一种数据结构&#xff0c;能够快速地进行存取。但是当遇到下面几种情况&#xff1a; ①我们需要存储的数据集数目是不定的 ②我们希望数据集能够自动排序 ③我们需要以键值对的方式存储数据 … 数组就不能满足我们的需求了。这时候&#xff0c;我们就需要使用…

边缘计算与云计算的不同,这篇说明白了!

术语“边缘计算”是指一种分布式计算&#xff0c;是将数据存储和计算带到需要它的站点或设备附近&#xff0c;这种分配设置消除了滞后时间并节省了带宽。与“物联网”相比&#xff0c;这是一种针对云环境的优化方法。它在数据源附近&#xff08;即网络的“边缘”&#xff09;处…

经典排序算法(12)——总结

一、排序算法简介 排序算法&#xff08;Sorting algorithm&#xff09;是一种能将一串数据&#xff0c;依照特定排序方式&#xff08;依照其中的某个或某些关键字的大小&#xff09;进行排列的一种算法。 常见的排序算法有&#xff1a;交换排序&#xff08;冒泡排序、快速排序&…

在Asp.Net Core MVC 开发过程中遇到的问题总结

1. Q: Razor视图中怎么添加全局模型验证消息A&#xff1a;使用ModelOnly<div asp-validation-summary"ModelOnly" class"text-danger"></div>2.Q&#xff1a;树形表格&#xff0c;使用的是bootstrap-tablejquery.treegridA&#xff1a;效果参考…

为什么子线程中不能直接更新UI

点击上方“dotNET全栈开发”&#xff0c;“设为星标”加“星标★”&#xff0c;每天11.50&#xff0c;好文必达全文约4000字&#xff0c;预计阅读时间8分钟当初有同事就碰到类似的问题&#xff0c;于是就总结了一些&#xff0c;那时写这篇文章是我还在第一家公司。今天有人提到…

解决问题的能力 10倍程序员

大家好&#xff0c;我是Z哥。今天我们聊的话题对大多数人来说应该都算是一个“痛点”&#xff0c;就是怎么提高自己解决问题的能力。我们的工作中&#xff0c;每天会遇到大大小小的很多问题。其中有些是之前从未遇到过的问题&#xff0c;这对很多人来说就会很棘手&#xff0c;不…

.NET Core 3.1正式发布,还不赶快升级!

点击蓝字关注我们 .NET Core 3.1于2019年12月3日正式发布&#xff0c;这是一个长期支持&#xff08;LTS&#xff09;版本&#xff0c;并且将支持三年&#xff0c;这个版本对.NET Core的许多方面进行了改进&#xff0c;建议您尽快升级。 .NET Core 3.1 的变更日志很小。唯一新增…

.NET Core Blazor 1-Blazor项目文件分析

简介Blazor是一个使用.NET技术用于代替JavaScript/typescript的前端WEB框架。在前端开发中使用.NET语言进行书写逻辑有利于我们的性能、可靠性和安全性。并且对于使用.NET开发人员而言&#xff0c;全栈的成本更低。截止文章发布时&#xff0c;.NET Core已经发布了3.1版本&#…

除了HTML、CSS与JS,现在WASM也是标准Web语言

大家应该知道&#xff0c;万维网联盟 W3C 认证的 Web 语言有 HTML、CSS 与 JavaScript&#xff0c;而近日联盟正式宣布 WebAssembly 核心规范&#xff08;WebAssembly Core Specification&#xff09;成为官方 Web 标准&#xff0c;这意味着 WebAssembly 成为了第 4 种 Web 语言…

DDD实战与进阶 - 值对象

概述作为领域驱动设计战术模式中最为核心的一个部分-值对象。一直是被大多数愿意尝试或者正在使用DDD的开发者提及最多的概念之一。但是在学习过程中&#xff0c;大家会因为受到传统开发模式的影响&#xff0c;往往很难去运用值对象这一概念&#xff0c;以及在对值对象进行持久…

C# Lazy Loading

前言按需加载对象延迟加载实际是推迟进行创建对象&#xff0c;直到对其调用后才进行创建初始化&#xff0c;延迟&#xff08;懒加载&#xff09;的好处是提高系统性能&#xff0c;避免不必要的计算以及不必要的资源浪费。常规有这些情况&#xff1a;对象创建成本高且程序可能不…

将 WinForms 应用从 .NET Core 3.0 升级到 3.1

点击上方蓝字关注“汪宇杰博客”导语我作为社区里的“拖控件之王”&#xff0c;拖控件贼心不死&#xff0c;有时候会维护一些老项目&#xff0c;其中包括一个2004年的WinForms 软件。9月份的时候我曾经将它迁移到了 .NET Core 3.0&#xff0c;因为代码实现完全没动&#xff0c;…

戴明博士:管理的十四项原则

爱德华兹戴明博士&#xff08;Dr. W. Edwards Deming&#xff09;于1982年首版发行的《走出危机》(Out of The Crisis)一书中&#xff0c;提出了组织管理的14条基本原则。书中戴明博士认为&#xff1a;当时的美国企业多致力于追求短期利润&#xff0c;缺乏不断推出新产品及完善…

在Windows系统中构建还原ASP.NET Core 源码

大家好&#xff0c;这几天试着从Github上拉取AspNetCore的源码&#xff0c;尝试着通过Visual Studio 打开&#xff0c;但是并不尽人意。我们需要去构建我们拉去的源代码&#xff0c;这样才可以通过VisualStudio可还原的项目。毕竟AspNetCore是一个巨型的项目集。先决条件在Wind…

用HttpReports快速搭建API分析平台

HttpReports简单介绍HttpReports 是 .Net Core下的一个Web组件&#xff0c;适用于 WebAPI 项目和 API 网关项目&#xff0c;通过中间件的形式集成到您的项目中, 通过HttpReports&#xff0c;可以让开发人员快速的搭建出一个 API 性能分析的基础报表网站。主要包含 HttpReports …

他,TypeScript GitHub Star 上海第一,全国第四!GitHub 总标星超两万!

前两天和老同学羡辙&#xff08;Apache Echarts 核心开发、百度最美工程师&#xff09;聊天。她分享了一个 GitHub 排名的网站给我。http://git-awards.com/users?typecity&languagetypescript&cityShanghai我看了下 TypeScript star 数量的排名。哇噻&#xff01;厉害…

[原]排错实战——拯救加载调试符号失败的IDA

本文之前发表的时候有些问题&#xff0c;作为强迫症患者的我又重新编辑后再次发表。如果您已经看过&#xff0c;请忽略。望见谅。缘起 最近想借助IDA逆向一个函数。在windows下&#xff0c;调试器&#xff08;比如vs, windbg&#xff09;可以通过调试符号&#xff08;PDB&#…

如何运用DDD - 实体

概述本文将介绍领域驱动设计&#xff08;DDD&#xff09;战术模式中另一个常见且非常重要的概念 - 实体。相对战术模式中其他的一些概念&#xff08;例如 值对象、领域服务等&#xff09;来说&#xff0c;实体应该比较容易让人理解和运用。但是我们如何去发现所在领域中的实体呢…

Natasha v2.5.4 版与运行时实战

文章转载授权级别&#xff1a;BNatasha 是一个十分便捷的动态构建库&#xff0c;支持.NET Standard2.0 / Core3.0 ; 比起繁杂的 IL 指令和 Expression 的众多 API , Natasha 的构建方式更加友好简洁&#xff0c; 基于 Natasha 可以让动态工作变得傻瓜&#xff0c;简单&#xf…

.Net Core3.1下使用Swagger搭建web api项目

前言&#xff1a;微软于前天发布.net core 3.1正式版,并将长期支持3.1。所以我听到这个消息后就急忙下载.net core 3.1的SDK和Runtime,应该是公司最先用3.1的攻城狮了????。OK&#xff01;废话少说&#xff0c;今天的目的是基于.net core 3.1建一个web api的项目先下载.net…