前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >身边的设计模式(二):工厂模式 与 DI

身边的设计模式(二):工厂模式 与 DI

作者头像
老张的哲学
发布2022-04-11 08:26:43
8640
发布2022-04-11 08:26:43
举报
文章被收录于专栏:NetCore 从壹开始

编者按:

定义一个用于创建对象的接口,让子类决定实例化哪一个类。

工厂方法使得一个类的实例化延迟到子类。

工厂模式,是迄今为止,使用最多,最广泛的设计模式之一,它的身影几乎出现在每一个框架和个人代码之中。 它是基石,只有充分了解并掌握了工厂模式,才能继续的向更深层的设计模式进发和努力。

一、什么是工厂模式?

在上一篇文章中,我们说到了《单例模式》,了解到了它的场景,也学会了它的目的,从模式类型上,我们可以知道,他是一个创建型的设计模式,说白了就是创建一个对象的实例,只不过是单例的 —— 单一实例的。

那今天我们继续说下一个创建型的设计模式 —— 工厂模式,工厂模式和单例模式,正好相反,他不是创建单一的,而是创建无穷的,随着项目而不断变化的实例场景。

工厂模式】,英文名称:Factory Pattern,是开发中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象

从日常使用分类上,工厂模式有三种: 1、简单工厂模式 2、工厂方法模式 3、抽象工厂模式

不过整体上,思路是类似的,可以统称为工厂模式,但是一般有人不认为简单工厂属于工厂模式,而且同时又把抽象工厂,给单独提取出来,作为一个新的设计模式,这个无可厚非,今天我们就从最简单代码开始,一步一步学习工厂模式,暂时先说下前两种,毕竟抽象工厂是单独的一个设计模式。

二、如何创建简单工厂模式

这里我不想用那些朦朦胧胧的例子来说明,比如说作饭,还是汽车,或者是手机生产等等,例子是可以,只不过和我们平时开发不是很贴近,我就用平时都在用的 Repository 仓储模式来说明下,而且我也打算下一步写一个能兼容所有 ORM 的 Repository Lib 组件库。

首先呢,我们平时开发的时候,如果想创建一个仓储方法,来调取数据库,很简单,直接上代码,每个人都能看懂:

代码语言:javascript
复制
 /// <summary>
 /// 定义仓储类
 /// </summary>
 public class Repository
 {
     /// <summary>
     /// 获取数据方法
     /// </summary>
     public void GetData()
     {
         // 可以进行各种操作,无论是EFCore的 DbSet ,
         // 还是Sqlsugar的 sugarClient
         // ...
         Console.WriteLine("获取全部数据!");
     }
 }

 // 调用
 [HttpGet]
 public object Get()
 {
     // 实例化仓储,然后调用
     Repository repository = new Repository();
     repository.GetData();
     return "";
 }

最后结果出来了:

是不是很简单?!没有任何含量,至于其他的什么上下文,咱们不管,只说调用情况,中间不论业务逻辑多复杂,咱们平时就是这么写的,也很符合我们平时开发的逻辑。打完收工,吃蛋糕!

可能你会说,工厂呢?设计模式呢?没有说呀,那我要提一个需求了,我们的项目需要用到多种 ORM 共存的仓储,嗯,你怎么办?你这个时候可能会说,简单!看我的:

代码语言:javascript
复制
 /// <summary>
 /// 定义一个 Sqlsugar 仓储
 /// </summary>
 public class RepositorySqlsugar
 {
     public void GetData()
     {
         Console.WriteLine("获取 Sqlsugar 全部数据!");
     }
 }

 /// <summary>
 /// 定义一个 EFCore 仓储
 /// </summary>
 public class RepositoryEFCore
 {
     public void GetData()
     {
         Console.WriteLine("获取 EFCore 全部数据!");
     }
 }

  // 然后我们调取
  [HttpGet]
  public object Get()
  {
      // 实例化仓储,然后调用
      RepositorySqlsugar repositorySqlsugar = new RepositorySqlsugar();
      repositorySqlsugar.GetData();
      RepositoryEFCore repositoryEFCore = new RepositoryEFCore();
      repositoryEFCore.GetData();

      return "";
  }

上边的代码也很简单,相信也有部分小伙伴用过,既然是两个ORM,那就定义两个操作类,互相没影响,还可以自定义扩展,然后分别去调用,没啥难度,这个时候,我们可以看到了效果:

设计模式中,要学会封装

上边的代码,我们可以解决多 ORM 共存的问题,不管设计的多粗糙,反正最后我们用到了 EFCore 和 SqlSugar 两个 ORM,你还可以自定义去调用,但是我们平时开发的时候知道,面向对象三大特性——封装、继承、多态。要学会去封装,不能随意的写很多的类,毕竟内容基本是一样的,好!那既然要封装,我们就抽象出来一个公共的 Repository,然后继承它就行了:

代码语言:javascript
复制
 /// <summary>
 /// 定义仓储抽象类
 /// </summary>
 public abstract class Repository
 {
     /// <summary>
     /// 抽象方法,获取数据
     /// </summary>
     public abstract void GetData();
 }


 /// <summary>
 /// 定义一个 EFCore 仓储
 /// 继承仓储父类
 /// </summary>
 public class RepositoryEFCore : Repository
 {
     /// <summary>
     /// 实现父类抽象方法
     /// </summary>
     public override void GetData()
     {
         Console.WriteLine("获取 EFCore 全部数据!");
     }
 }

/// <summary>
/// 定义一个 Sqlsugar 仓储
/// </summary>
public class RepositorySqlsugar : Repository
{
    public override void GetData()
    {
        Console.WriteLine("获取 Sqlsugar 全部数据!");
    }
}

这个时候相信大家都能明白了,我们用两个类,去继承抽象仓储父类,使用的时候,只需要根据需要,实例不同的仓储子类就行了,这样就可以任意的使用:

代码语言:javascript
复制
 /// <summary>
 /// 根据不同的类型,来实例化不同的对象
 /// </summary>
 /// <param name="type"></param>
 /// <returns></returns>
 public Repository GetRepository(string type)
 {
     Repository repository = null;
     if (type.Equals("sugar"))
     {
         repository = new RepositorySqlsugar();
     }
     else if (type.Equals("efcore"))
     {
         repository = new RepositoryEFCore();
     }
     return repository;
 }

 // 然后去调用
 [HttpGet]
 public object Get()
 {
     // 实例化仓储,然后调用
     Repository sugar = GetRepository("sugar");
     sugar.GetData();

     Repository efcore = GetRepository("efcore");
     efcore.GetData();

     return "";
 }

然后我们直接来看看效果:

依然可以!怎么样!是不是有那么点儿意思了,是不是有设计感了,概括来说,我们通过一个方法,来控制我们的不同对象的实例输出,从而实现封装、继承、多态!

到了这里,我们可以长舒一口气,工厂模式这么简单的么?!不,还远远没有到,我再来问一个问题,项目我的 api 层虽然封装了这个公共仓储方法,来控制输出,但是我们也同时依赖了这些子类仓储,你想想是不是,这个时候我们就稍微简单的修改一下就好 —— 为了降低对象之间的耦合。

降低对象之间的耦合

那这个时候我们应该怎么办呢,很简单,建一个静态方法就行了,这样就提取出去了:

代码语言:javascript
复制
/// <summary>
/// 定义简单仓储工厂
/// </summary>
public class RepositorySimpleFactory
{
    /// <summary>
    /// 定义静态方法,控制实例对象输出
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static Repository GetRepository(string type)
    {
        Repository repository = null;
        if (type.Equals("sugar"))
        {
            repository = new RepositorySqlsugar();
        }
        else if (type.Equals("efcore"))
        {
            repository = new RepositoryEFCore();
        }
        return repository;
    }
}

 [HttpGet]
 public object Get()
 {
     // 静态方法获取不同实例,对象之间降低耦合
     Repository sugar = RepositorySimpleFactory.GetRepository("sugar");
     sugar.GetData();

     Repository efcore = RepositorySimpleFactory.GetRepository("efcore");
     efcore.GetData();

     return "";
 }

结果不用看了,依然是有效的,没错,你已经不知不觉中学会了简单工厂模式!这次真的很简单了,从上边我们看出来了,我们一步一步的往下走,相信每个人都能看懂他的意义,这个时候,我们可以骄傲的说,我们学会了简单工厂!

但是我们的目的,是工厂方法,简单工厂只是一个开胃菜,这个时候,我们稍微缓一缓,如果上边的你都看懂了,可以思考一个问题,如果没看懂,请继续刷第二遍,

那问题就是:

简单工厂,我们是通过一个变量 type 来进行区分的,来创建不同的子类对象实例的,这样不好,因为如果我们以后要增加 dapper 的话,我们还是需要修改这个简单工厂里的方法,如果很多的话,不仅麻烦,也不符合我们的六大设计模式原则中的其中一个原则,就是OCP原则,中文是【开放关闭原则】,对增加代码开发,对修改代码关闭。

六大原则: 【单一职责原则】,【里氏替换原则】,【依赖倒置原则】,【接口隔离原则】、【迪米特法则】和【开闭原则】

那我们应该如何做,才能满足我们的对修改关闭,对添加开放的原则呢?

没错,就是今天的重头戏 —— 工厂方法模式!

三、如何创建工厂方法模式

刚刚我们文章的开头,我们说到了,我们可以通过定义多个类,来进行不同 ORM 的划分,然后我们也封装了一个GetRepository 的方法,实现了【哪里有变化,哪里就封装】的设计思路,那我们可以借着这个思路,我们可以建立多个工厂来实现。

说白了就是,我们抽象出来一个工厂,这个工厂用来生产不同的 Repository 对象实例,从而实现目的,那我们就直接开始动手:

代码语言:javascript
复制
 /// <summary>
 /// 1、抽象工厂类
 /// </summary>
 public abstract class RepositoryFactory
 {
     /// <summary>
     /// 抽象方法,用来返回仓储对象
     /// </summary>
     /// <returns></returns>
     public abstract Repository CreateRepository();
 }

 /// <summary>
 /// 2.1、EFCore 的仓储工厂
 /// 继承抽象仓储工厂
 /// </summary>
 public class RepositoryFactory_EFCore : RepositoryFactory
 {
     /// <summary>
     /// 重写,生成EFCore 仓储的实例
     /// </summary>
     /// <returns></returns>
     public override Repository CreateRepository()
     {
         return new RepositoryEFCore();
     }
 }

/// <summary>
/// 2.2、SqlSugar 的仓储工厂
/// 继承抽象仓储工厂
/// </summary>
public class RepositoryFactory_SqlSugar : RepositoryFactory
{
    /// <summary>
    /// 重写,生成 SqlSugar 仓储的实例
    /// </summary>
    /// <returns></returns>
    public override Repository CreateRepository()
    {
        return new RepositorySqlsugar();
    }
}

这里我们可以看到,我们是在仓储的基础上,抽象了一套工厂模式,从之前我们通过 type 类型来判断,生成仓储实例,变成了,通过不同的仓储工厂来生成对应仓储对象实例。

然后我们来调用一下:

代码语言:javascript
复制
 [HttpGet]
 public object Get()
 {
     // 初始化创建Repository的两个仓储工厂
     RepositoryFactory efcoreRepositoryFactory = new RepositoryFactory_EFCore();
     RepositoryFactory sugarRepositoryFactory = new RepositoryFactory_SqlSugar();

     // 生产efcore仓储的实例
     var efcoreRepository = efcoreRepositoryFactory.CreateRepository();
     efcoreRepository.GetData();

     //生产sugar仓储的实体
     var sugarRepository = sugarRepositoryFactory.CreateRepository();
     sugarRepository.GetData();

     return "";
 }

结果依然正确:

好啦!这次可以真正的歇歇了,工厂方法模式,已经正式完成了,可能越往后越复杂,不过自己简单想一想,还是能够明白的。

你可能会说,说了这么多,这个和文章开头,我们定义两个仓储类,直接输出不一样么,现在搞的这么复杂:

真的有必要么?当然!这个还是一个小的项目,如果你看看我的 Blog.Core 项目就知道了,那么小的还有十多个仓储,中型项目几十个上百个,然后呢,如果我们要同时使用四种 ORM:EFCore、SqlSugar、Dapper、Ado 等等等等,还有事务操作,所以工厂方法模式,还是很有必要的,除了简单代码量,而且更符合我们开发设计思想:封装,继承,多态,OCP原则等等。

上边我们建立好了工厂方法,如果我们现在需要创建一个 Dapper 的ORM,我们什么都不需要修改,只需要添加几个类即可:

1、定义一个 RepositoryDapper 仓储类; 2、定义一个 RepositoryFactory_Dapper 仓储工厂类;

为了验证大家的学习是否成功,代码我就不写了,自己可以练习哟,有问题,欢迎留言。

四、工厂方法真的完美了么

上边我们看到了,很简单,只需要我们配置两个类,就可以任意的扩展了,但是!

有没有发现很麻烦?!需要定义好几个类,虽然封装了,但是还是想再完善下,那有没有办法呢?

有!那就是【抽象工厂模式】,可以轻松的解决复杂度和类的依赖,

请听下回分解。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 NetCore 从壹开始 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、什么是工厂模式?
  • 二、如何创建简单工厂模式
    • 设计模式中,要学会封装
      • 降低对象之间的耦合
      • 三、如何创建工厂方法模式
      • 四、工厂方法真的完美了么
      相关产品与服务
      对象存储
      对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档