从壹开始 [ Design Pattern ] 之三 ║ 工厂模
正文
编者按:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到子类。
工厂模式,是迄今为止,使用最多,最广泛的设计模式之一,它的身影几乎出现在每一个框架和个人代码之中。
它是基石,只有充分了解并掌握了工厂模式,才能继续的向更深层的设计模式进发和努力。
今天的可能有点儿绕,如果有不懂的,可以在文章下边留言,如果有三个以上评论要我讲课的话,我就在直播课堂,给大家讲解一下。
同时为了更好的讲解,我会稍微穿插一个小故事,让大家能够很好的掌握工厂模式的三大步。
一、什么是工厂模式?
在上一篇文章中,我们说到了《单例模式》,了解到了它的场景,也学会了它的目的,从模式类型上,我们可以知道,他是一个创建型的设计模式,说白了就是创建一个对象的实例,只不过是单例的 —— 单一实例的。
那今天我们继续说下一个创建型的设计模式 —— 工厂模式,工厂模式和单例模式,正好相反,他不是创建单一的,而是创建无穷的,随着项目而不断变化的实例场景。
【工厂模式】,英文名称:Factory Pattern,是开发中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,****我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
从日常使用分类上,工厂模式有三种:
1、简单工厂模式
2、工厂方法模式
3、抽象工厂模式
不过整体上,思路是类似的,可以统称为工厂模式,但是一般有人不认为简单工厂属于工厂模式,而且同时又把抽象工厂,给单独提取出来,作为一个新的设计模式,这个无可厚非,今天我们就通过一个生活中的小栗子,然后再配合具体代码,一步一步学习工厂模式,从简单工厂,到工厂方法,最后到抽象工厂。
二、简单工厂模式
生活小场景第一集:
老张呢,平时都是一个人住,为了健康生活,经常会自己买菜,洗衣,做饭,周末时不时的就像来点儿花样(其实并没有😂),比如上周末想吃【椒盐紫苏虾】,是不是听着名字就想吃,嗯,就是不好做,辛辛苦苦搞了半天,最后还是不太满意。然后呢,这周末又想吃【红烧话梅排骨】挑战一下?结果到了周末,还是停留在想想的阶段,想吃但是不想做!
这个时候,就特别想要一个我的厨师,我吃啥,就给我做啥,就是这么美好,只要我传递一个菜名参数,就会给我实例化一个菜,是真正的菜的对象示例,不是一个臆想,那作为代码的上帝,我们来创建一个吧,一个简单的个人厨师——简单工厂。
1、先从最简单的开始
上边的例子大家肯定都能看懂,我就不多说了,代码呢,本来想用这个小故事里的,只不过和我们平时开发不是很贴近,我就用平时都在用的 Repository 仓储模式来说明下,而且我也打算下一步写一个能兼容所有 ORM 的 Repository Lib 组件库。
首先呢,我们平时开发的时候,如果想创建一个仓储方法,来调取数据库,很简单,直接上代码,每个人都能看懂:
[ 复制代码](javascript:void(0); "复制代码")
<pre> /// <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 "";
}</pre>
](javascript:void(0); "复制代码")
最后结果出来了:
image是不是很简单?!没有任何含量,至于其他的什么上下文,咱们不管,只说调用情况,中间不论业务逻辑多复杂,咱们平时就是这么写的,也很符合我们平时开发的逻辑。打完收工,吃蛋糕!
可能你会说,工厂呢?设计模式呢?没有说呀,那我要提一个需求了,我们的项目需要用到多种 ORM 共存的仓储,嗯,你怎么办?你这个时候可能会说,简单!看我的:
[ 复制代码](javascript:void(0); "复制代码")
<pre> /// <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 "";
}</pre>
](javascript:void(0); "复制代码")
上边的代码也很简单,相信也有部分小伙伴用过,既然是两个ORM,那就定义两个操作类,互相没影响,还可以自定义扩展,然后分别去调用,没啥难度,这个时候,我们可以看到了效果:
image2、 设计模式中,要学会封装
上边的代码,我们可以解决多 ORM 共存的问题,不管设计的多粗糙,反正最后我们用到了 EFCore 和 SqlSugar 两个 ORM,你还可以自定义去调用,但是我们平时开发的时候知道,面向对象三大特性——封装、继承、多态。要学会去封装,不能随意的写很多的类,毕竟内容基本是一样的,好!那既然要封装,我们就抽象出来一个公共的 Repository,然后继承它就行了:
[ 复制代码](javascript:void(0); "复制代码")
<pre> /// <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 全部数据!");
}
}</pre>
](javascript:void(0); "复制代码")
这个时候相信大家都能明白了,我们用两个类,去继承抽象仓储父类,使用的时候,只需要根据需要,实例不同的仓储子类就行了,这样就可以任意的使用:
[ 复制代码](javascript:void(0); "复制代码")
<pre> /// <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 "";
}</pre>
[ 复制代码](javascript:void(0); "复制代码")
然后我们直接来看看效果:
image依然可以!怎么样!是不是有那么点儿意思了,是不是有设计感了,概括来说,我们通过一个方法,来控制我们的不同对象的实例输出,从而实现封装、继承、多态!是不是很像我们的个人管家,只要传递过去一个参数,就可以给我们生成多个对象实例。
到了这里,我们可以长舒一口气,工厂模式这么简单的么?!不,还远远没有到,我再来问一个问题,项目我的 api 层虽然封装了这个公共仓储方法,来控制输出,但是我们也同时依赖了这些子类仓储,你想想是不是,这个时候我们就稍微简单的修改一下就好 —— 为了降低对象之间的耦合。
3、降低对象之间的耦合
那这个时候我们应该怎么办呢,很简单,建一个静态方法就行了,这样就提取出去了:
[ 复制代码](javascript:void(0); "复制代码")
<pre>/// <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 "";
}</pre>
[ 复制代码](javascript:void(0); "复制代码")
结果不用看了,依然是有效的,没错,你已经不知不觉中学会了简单工厂模式!这次真的很简单了,从上边我们看出来了,我们一步一步的往下走,相信每个人都能看懂他的意义,这个时候,我们可以骄傲的说,我们学会了简单工厂!
但是我们的目的,是工厂方法,简单工厂只是一个开胃菜,这个时候,我们稍微缓一缓,如果上边的你都看懂了,可以思考一个问题,如果没看懂,请继续刷第二遍,
那问题就是:
简单工厂,我们是通过一个变量 type 来进行区分的,来创建不同的子类对象实例的,这样不好,因为如果我们以后要增加 dapper 的话,我们还是需要修改这个简单工厂里的方法,如果很多的话,不仅麻烦,也不符合我们的六大设计模式原则中的其中一个原则,就是OCP原则,中文是【开放关闭原则】,对增加代码开发,对修改代码关闭。
六大原则:
【单一职责原则】,【里氏替换原则】,【依赖倒置原则】,【接口隔离原则】、【迪米特法则】和【开闭原则】
那我们应该如何做,才能满足我们的对修改关闭,对添加开放的原则呢?
没错,就是今天的第一个重头戏 —— 工厂方法模式!
三、工厂方法模式
生活场景第二集:
老张吃中餐吃的越来越多了,有点儿腻了,那咋办,换个口味呗,打算吃西餐了,比如【鱼子酱】,比如【奶油蛋糕】,比如【法式焗蜗牛】,听着还不错,直接安排!
我不是有一个私人厨师嘛,交给他,反正只要我们传递一个菜名,就能创建实例,多好。
然后过了一段时间,厨师离职了。🙃
大家肯定也能猜得出来,第一是自己忙不过来,第二就是以后我们创建任何一个新的对象实例的时候,都需要修改内部方法,让这个简单工厂越来越大,越来越笨重,违背了我们的OCP原则。
后来我想通了,既然是吃货,就直接再招一个西点师傅吧,负责西餐,中餐师傅做中餐,各司其职,然后再来一个个人助理,统一协调。
只要我想吃一道菜,私人助理就会自动给我创建指定的厨师,这就是工厂方法模式。
甚至以后我想再吃一个风格的菜,比如面点,直接让个人助理招一个面点师就行,实现了对内修改关闭,对外新增开放的原则。
刚刚我们文章的开头,我们说到了,我们可以通过定义多个类,来进行不同 ORM 的划分,然后我们也封装了一个GetRepository 的方法,实现了【哪里有变化,哪里就封装】的设计思路,那我们可以借着这个思路,我们可以建立多个工厂来实现。
说白了就是,我们抽象出来一个工厂,这个工厂用来生产不同的 Repository 对象实例,从而实现目的,那我们就直接开始动手:
[ 复制代码](javascript:void(0); "复制代码")
<pre> /// <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();
}
}</pre>
](javascript:void(0); "复制代码")
这里我们可以看到,我们是在仓储的基础上,抽象了一套工厂模式,从之前我们通过 type 类型来判断,生成仓储实例,变成了,通过不同的仓储工厂来生成对应仓储对象实例。
然后我们来调用一下:
[ 复制代码](javascript:void(0); "复制代码")
<pre> [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 "";
}</pre>
](javascript:void(0); "复制代码")
结果依然正确:
image好啦!这次可以真正的歇歇了,工厂方法模式,已经正式完成了,可能越往后越复杂,不过自己简单想一想,还是能够明白的。
你可能会说,说了这么多,这个和文章开头,我们定义两个仓储类,直接输出不一样么,现在搞的这么复杂:
image真的有必要么?当然!这个还是一个小的项目,如果你看看我的 Blog.Core 项目就知道了,那么小的还有十多个仓储,中型项目几十个上百个,然后呢,如果我们要同时使用四种 ORM:EFCore、SqlSugar、Dapper、Ado 等等等等,还有事务操作,所以工厂方法模式,还是很有必要的,除了简单代码量,而且更符合我们开发设计思想:封装,继承,多态,OCP原则等等。
上边我们建立好了工厂方法,如果我们现在需要创建一个 Dapper 的ORM,我们什么都不需要修改,只需要添加几个类即可:
1、定义一个 RepositoryDapper 仓储类;
2、定义一个 RepositoryFactory_Dapper 仓储工厂类;
为了验证大家的学习是否成功,代码我就不写了,自己可以练习哟,有问题,欢迎留言。
大家学到了这里,我建议自己可以练习练习,可以写一下部分代码,同时呢,也很好的区分下【简单工厂】和【工厂方法】之间的区别和练习,如果说你已经看懂了,或者说自己已经练习好了,那咱们就继续往下说今天的第二个重要知识点 —— 抽象工厂;
再说抽象工厂之前呢,咱们先简单总结一下:
1、我们知道,工厂模式属于创建型开发模式的一元,他的作用就是创建我们需要的对象,如果一个一个创建的话,会很麻烦,所以我们诞生出来了一个【简单工厂】,这个简单工厂只是简单的人为的把几个对象的实例给堆起来,通过type 来区分,然后分别 new 实例化,有时候也是一个很好的方案,但是这样有一个弊端,违背了我们开发六大原则中的——OCP开放关闭原则,所以这个时候,我们就又多出来一个新的概念【工厂方法】。
2、【工厂方法】是在【简单工厂】的基础上,做了相应的改良——通过多个子类的形式,来替换之前的 type 分类法,对内修改关闭,对外新增打开,这样无论是代码整洁上,还是扩展封装上,都有了很好的体验,同时也满足了我们的OCP开发原则。
3、但是!这种方案真的就很好了么,我们再来会议一下,我们无论是简单工厂,还是工厂方法,都是生成的单独的一个类,好处是可以一一的慢慢扩展,但归根结底还是在处理****一个类,我们平时开发的时候,处理一个类是很多,比如常见的helper,Log 之类的。但是这不是我们开发的重点,我们平时使用最多的还是 Service 类,或者 Repository 类,里边有很多,各种各样的的类,比如 User 表,Role 表,Permission 表等等,每一个实体又都对应各自的一个服务类或者仓储类。
4、那这个时候,我们使用上边的【工厂方法】还行么?肯定是不行的!因为我们上边是一个二维体系,EFCoreRepository 、SugarRepository、DapperRepository等等,是这样的二维,我们现在要做的就是在这个二维的基础上,再加上一个维度,就是要解决 User 、Role、Permission 实体等等这种一组或者一系列的类创建的问题。
5、那就是今天下边要说到的【抽象工厂】模式。
注意,下边的例子可能不太恰当,只是作为理解抽象工厂模式来使用,具体开发中,可能有出入。
四、抽象工厂模式
生活场景第三集:
老张生活的美滋滋,天天不愁吃,嗯还押上了🤣,有一天老李也想要厨师,那咋办,我大笔一挥,简单!安排上。
我就通知我的个人助理,帮老李也找了三个厨师——中餐,西餐,面点,那现在就有六个了。
就这样过了一段时间,个人助理辞职了?!
情况就是,他说太忙了,而且需求也是各种各样,相同的一个系列,需要准备很多,还有我的其他的小伙伴也让他来找厨师,好几组,自己的小本本上,密密麻麻的都是记录的各种安排。
明白了,如果是一系列的,一组对象实例创建的话,如何还用工厂方法的话,就会出问题,而且还同时破坏了OCP原则,大家想想是不是,对内修改关闭原则
那这个时候怎么办呢,欸!我再抽象一层,把我的这个个人助理,也就是这个工厂,给增加一个大管家中介,这里是不是又有了一个中介者模式的味道😂,所以说,设计模式都有相似性。
那这个大管家中介,就是一个抽象工厂,他负责管理多个组多个系列,也就是对应多个私人助理,以及下边的厨师。
上边的问题我们都看到了,我们要解决一系列一组类创建的问题,引申出来了抽象工厂模式,那下边我们就简单写一些代码,看看是否跑的通。
1、创建一个核心层,添加多个仓储操作
我们毕竟要操作数据库嘛,所以肯定需要仓储来持久化,那我们就创建一个 FactoryPattern.Core 层,用来存放我们整个项目核心的底层。首先要创建的就是几个仓储:
[ 复制代码](javascript:void(0); "复制代码")
<pre>/// <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
{
}</pre>
](javascript:void(0); "复制代码")
那基本的仓储都已经定义好了,现在就需要一个工厂来生产这一系列产品了,所以我们定义一个抽象工厂类:
[ 复制代码](javascript:void(0); "复制代码")
<pre> /// <summary>
/// 抽象工厂类,提供创建不同仓储接口 /// </summary>
public abstract class AbstractFactory
{ // 抽象工厂提供创建一系列产品的接口
public abstract UserRepository UserRepository(); public abstract RoleRepository RoleRepository(); public abstract PermissionRepository PermissionRepository();
}</pre>
](javascript:void(0); "复制代码")
结构如下:
image2、创建EFCore仓储工厂层
说人话就是,刚刚我们不是定义了一个抽象的工厂么,用来生产我们数据库中一系列一组的产品,也就是数据库表,那现在我们就需要指定具体的工厂来生产他们了,首先第一个我们就用EFCore这个工厂来生产,创建一个 FactoryPattern.Repository.EFCore 类库,并引用 Core 核心层
image首先呢,我们就要在这一层中,对那几个抽象的仓储类做重写,对应每一个EFCore 版本的仓储类,可能你会问为什么,要每一个重写下,还是OCP原则,而且还有一个愿意,Sqlsugar 可能某些表达式查询,在EFCore里不能用,所以必须每一个重写出来。
这里有一个地方就是,可以在EFCore也针对基类仓储做一个基类的,但是后来有类型不一致问题,大家可以自己看看.
[ 复制代码](javascript:void(0); "复制代码")
<pre> /// <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();
}
} // 其他两个表也是这个情况,不粘贴代码了。</pre>
](javascript:void(0); "复制代码")
那现在有了子仓储产品了,我们就开始加工生产了,创建 EFCoreRepositoryFactory.cs ,并继承抽象工厂,同时实现抽象方法:
[ 复制代码](javascript:void(0); "复制代码")
<pre> /// <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();
}
}</pre>
](javascript:void(0); "复制代码")
结构如下:
image3、同理,创建Sugar仓储工厂层
过程和上边的一模一样,我就不多说了,整体结构还是这样的:
image4、控制器调用实例
我们在 api 层,引用刚刚创建的两个仓储层项目:
image然后开始调用:
[ 复制代码](javascript:void(0); "复制代码")
<pre>[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();
}</pre>
](javascript:void(0); "复制代码")
结果我就不调试,肯定是没有问题的,毕竟仅仅是类的调用嘛,如果说你从上边往下看,看到了这里,还没有问题,并且能大概明白其中的意义,那你的工厂模式已经完全学会了,特别是这个抽象工厂,一直很绕,而且也使用的不是很直观,用途不是很多。
5、抽象工厂和工厂方法的区别
现在我们再简单的说明一下,我们通过【抽象工厂】模式,慢慢的明白了,其实抽象工厂是在【工厂方法】模式的基础上,往外又多做了一套封装,目的就是解决生产一系列产品的时候,工厂方法无法满足的问题。
所以这个时候,如果有人问你二者的区别,核心的区别就是:如果是一个产品用工厂方法,一系列产品,用抽象工厂。
创建一个产品用工厂方法;
创建一系列产品用抽象工厂;
但是虽然解决了问题,还是有很多的问题的,单单从上边我们创建了那么多的子类,以及子类必须重写这一点来看,这么设计肯定有弊端,那有什么改进的呢?咱们继续往下看。
五、抽象工厂与依赖注入
这里我就不详细说了,其实就是一个思路的用法,这里举个例子就行了,大家肯定都用过三层架构,其中有一个数据访问层 DALFactory ,我们平时使用的时候,就是直接把类的实例给 return 出来,如果我们同时连接多个数据库呢?那这样的话,我们就像上边说到的,建立多个 DAL 层,比如 DALSqlServer、DALMysql 等等,那我们如何通过接口来获取服务呢,就是通过反射指定的程序集来实现,这个就是简单的使用了抽象工厂。
比如这个网上的图片,就是这个意思,大家看个意思就行:
image说到这里大家有没有了解到一些小小的心得,似乎这个和有一个东西很像!对!就是我们平时使用的依赖注入。其实我们可以想一想,我们在服务注册的时候,通过反射将多个服务注册到容器里,然后我们再使用的时候,是容器通过接口别名,给我们找到指定的具体服务,甚至也实现了一个接口,多个服务的操作,这个就是工厂模式和依赖注入的小小的关系。
工厂模式和依赖容器的区别:
factory,di container,service locator都是对象的创建者或者提供者,为什么是不同的名字,不是因为它们提供对象的实现,而是取决于你如何使用。你通过注入的方编程,让框架利用它提供对象,它就是di container,你显式利用它来提供你所需要的对象,它就叫做service locator。
总结一下:
今天我们通过简单的代码,一步一步,从【简单工厂】开始,了解到了多个类是如何创建的,然后明白了【工厂方法】模式,更好的实现了OCP原则,也实现了封装多态的原理,接下来咱们通过【抽象工厂】的举例,进一步对一系列一组产品生产的时候,所采用的方案,到最后,我们简单的说明了一下反射以及依赖注入和工厂模式的关系,可能读起来还是有点儿凌乱,不过我还是简单大家多多的学学,查查资料,因为我认为,设计模式是结构的基础,而工厂模式又是设计模式的基础,可见其重要性,如果看不懂没关系,等我直播讲课吧。
当然抽象工厂也是有一些弊端的,比如(摘抄百度):
【3.1】、****优点:【抽象工厂】模式将系列产品的创建工作延迟到具体工厂的子类中,我们声明工厂类变量的时候是使用的抽象类型,同理,我们使用产品类型也是抽象类型,这样做就尽可能的可以减少客户端代码与具体产品类之间的依赖,从而降低了系统的耦合度。耦合度降低了,对于后期的维护和扩展就更有利,这也就是【抽象工厂】模式的优点所在。可能有人会说在Main方法里面(这里的代码就是客户端的使用方)还是会使用具体的工厂类,对的。这个其实我们通过Net的配置,把这部分移出去,最后把依赖关系放到配置文件中。如果有新的需求我们只需要修改配置文件,根本就不需要修改代码了,让客户代码更稳定。依赖关系肯定会存在,我们要做的就是降低依赖,想完全去除很难,也不现实。
【3.2】、缺点:有优点肯定就有缺点,因为每种模式都有他的使用范围,或者说要解决的问题,不能解决的问题就是缺点了,其实也不能叫缺点了。【抽象工厂】模式很难支持增加新产品的变化,这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这样也就违背了“开发——封闭”原则。
【3.3】、抽象工厂模式的****使用场景: 如果系统需要多套的代码解决方案,并且每套的代码方案中又有很多相互关联的产品类型,并且在系统中我们可以相互替换的使用一套产品的时候可以使用该模式,客户端不需要依赖具体实现。
六、示例代码
https://github.com/anjoy8/DesignPattern/tree/master/FactoryPattern
1、Dependency Injection vs Factory Pattern
一起学习,一起进步 QQ群:867095512
关注我哦