编者按:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。
工厂方法使得一个类的实例化延迟到子类。
工厂模式,是迄今为止,使用最多,最广泛的设计模式之一,它的身影几乎出现在每一个框架和个人代码之中。 它是基石,只有充分了解并掌握了工厂模式,才能继续的向更深层的设计模式进发和努力。
在上一篇文章中,我们说到了《单例模式》,了解到了它的场景,也学会了它的目的,从模式类型上,我们可以知道,他是一个创建型的设计模式,说白了就是创建一个对象的实例,只不过是单例的 —— 单一实例的。
那今天我们继续说下一个创建型的设计模式 —— 工厂模式,工厂模式和单例模式,正好相反,他不是创建单一的,而是创建无穷的,随着项目而不断变化的实例场景。
【工厂模式】,英文名称:Factory Pattern,是开发中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
从日常使用分类上,工厂模式有三种: 1、简单工厂模式 2、工厂方法模式 3、抽象工厂模式
不过整体上,思路是类似的,可以统称为工厂模式,但是一般有人不认为简单工厂属于工厂模式,而且同时又把抽象工厂,给单独提取出来,作为一个新的设计模式,这个无可厚非,今天我们就从最简单代码开始,一步一步学习工厂模式,暂时先说下前两种,毕竟抽象工厂是单独的一个设计模式。
这里我不想用那些朦朦胧胧的例子来说明,比如说作饭,还是汽车,或者是手机生产等等,例子是可以,只不过和我们平时开发不是很贴近,我就用平时都在用的 Repository 仓储模式来说明下,而且我也打算下一步写一个能兼容所有 ORM 的 Repository Lib 组件库。
首先呢,我们平时开发的时候,如果想创建一个仓储方法,来调取数据库,很简单,直接上代码,每个人都能看懂:
/// <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 共存的仓储,嗯,你怎么办?你这个时候可能会说,简单!看我的:
/// <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,然后继承它就行了:
/// <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 全部数据!");
}
}
这个时候相信大家都能明白了,我们用两个类,去继承抽象仓储父类,使用的时候,只需要根据需要,实例不同的仓储子类就行了,这样就可以任意的使用:
/// <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 层虽然封装了这个公共仓储方法,来控制输出,但是我们也同时依赖了这些子类仓储,你想想是不是,这个时候我们就稍微简单的修改一下就好 —— 为了降低对象之间的耦合。
那这个时候我们应该怎么办呢,很简单,建一个静态方法就行了,这样就提取出去了:
/// <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 对象实例,从而实现目的,那我们就直接开始动手:
/// <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 类型来判断,生成仓储实例,变成了,通过不同的仓储工厂来生成对应仓储对象实例。
然后我们来调用一下:
[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 仓储工厂类;
为了验证大家的学习是否成功,代码我就不写了,自己可以练习哟,有问题,欢迎留言。
上边我们看到了,很简单,只需要我们配置两个类,就可以任意的扩展了,但是!
有没有发现很麻烦?!需要定义好几个类,虽然封装了,但是还是想再完善下,那有没有办法呢?
有!那就是【抽象工厂模式】,可以轻松的解决复杂度和类的依赖,
请听下回分解。
本文分享自 NetCore 从壹开始 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!