前面我们学习了工厂模式和工厂方法模式,下面做一个简单的回顾:
简单工厂模式:是为了将具体类的实例化和这个类对象的应用分离出来,
工厂方法模式:是为了让简单工厂方法满足”对扩展开放,对修改关闭”,将对象的创建推迟到子类中
下面我们要学习的就是工厂模式家族中最后一个设计模式抽象工厂模式。抽象工厂模式提供了一个创建一系列相关对象或者一系列相互依赖对象的接口。抽象工厂定义了一系列的用来生产不同等级结构下的同一产品族接口,不需要关心具体产品是什么,这样一来将客户从具体的产品中解耦出来。
1:什么是抽象工厂模式
定义:提供了一个接口,用于创建相关的或相互依赖的一组对象家族
类图:
抽象工厂接口(IFactory):定义了一组方法,每个方法都用来创建一个具体产品
具体工厂(Factory):实现了产品接口, 完成具体产品的创建
产品A/B抽象类(Abstract Product A/B):是一个抽象类,用于具体工厂来生产产品
具体产品A/B类(Concrete Product A/B):具体工厂生产出来的产品
业务场景:
继续我门的月饼加盟店案例,随着我们的品牌越来越好,每个城市都出现了很多的线下店铺,但同时也出现了一个新的问题那就是店铺使用的原料(面粉,油等)的不正规现象。所以为了保证我们的品质,保证每家店铺的原料都和安全合格有机的,我不得不继续升级我们的项目,来保证所有的原料都出至一家,都由总部统一配送。
2:实现抽象工厂模式
分析场景:
在我们上面描述的场景中,每个月饼都需要面粉和油,但是不同区域使用的面粉和油不一样。那我们的(高筋面粉,中筋面粉,低筋面粉),(胡麻油,色拉油,大豆油)就够成了三个不同的产品族,同时面粉和油便认为是我们的产品等级。如下图:
通过上面的分析,我就可以确定我们的产品族和产品等级了,那我们的IFactory就只需要提供2个产品的生产接口,我门的实体工厂ConcreteFactory需要3个来生产本地化的具体产品,下面针对我们的场景来设计一份我们自己的类图:
这样一来,我们就完美的把这个场景映射到了抽象工厂模式中,这样一来,我们便解决了原料的安全隐患,接下来让我们来思考一下,如何设计我们的月饼,线下商铺,让他们完美协作。
首先我们需要一个线下的实体店方便我们点餐,每个实体店都需要生产月饼也就意味着每个实体店都需要再维护一个原料工厂。依据我们的依赖倒置原则,抽象一个月饼类和一个月饼店的接口,类图设计如下:
完成上面复杂的分析后剩下的工作就是用code来实现我们的类图设计。
定义工厂接口,用来生产一组面粉和油
定义实体工厂SCFactory,来生产低筋面粉和色拉油
定义实体工厂NMFactory,来生产高筋面粉和胡麻油
定义实体工厂BJFactory,来生产中筋面粉和大豆油
定义我们面粉类产品
定义我们的油类产品
定义我们的月饼家族
定义我们的客户端,实体加盟店铺
好了,可以下单了,走进内蒙地区加盟店,点个肉陷月饼
工程介绍:
IFactory:月饼的原料工厂接口,定义生产一个月饼需要的相关产品的家族创建,
SCFactory,NMFactory,BJFactory:负责生产具体的原料,每个工厂都会生产符合自己的产品原料
Dough,HighDough,MiddleDough,LowDough:面粉的家族
Oil,FlaxOil,SoybeanOil,SaladOil:油的家族
MoonCakeSotre:线下加盟店铺,用来生产具体区域的月饼(如:内蒙店铺就只需要内蒙的工厂来提供的面粉和油,符合当地的口味)
3:抽象工厂的使用场景
切换数据库或者添加新的数据库:
抽象工厂就会突显的尤为重要,例如:我们工程中有三个DAO:DAO1, DAO2, DAO3;但是在不用的数据库(Sqlsever, my Sql, Access)中语法有些小的差异,那当我们要切换数据库就需要把每个DAO里面的SQL都修改成相应的语法,这样的工程就违反了我们的“开闭原则“。让我们使用抽象工程来解决这个问题:
1:我们创建一个IFacotry定义一组生成DAO层的接口
2:创建我门的各个产品类和对应的接口(在代码中要依赖抽象,不依赖具体对象),IDAO1,DAO1, IDAO2,DAO2, IDAO3, DAO3
3:创建三个SQL的具体工厂,(Sql Server Factroy, MySqlFactory, Access Factory)实现IFactory接口并返回具体的产品对象
如果我们需要添加/切换新的数据库,就只需要添加新的DAO层实现和新的Factory并实现IFactory接口就ok,不需要修改任何代码。
4:抽象工厂的优缺点
优点:
1):抽象工厂可以把一系列相关的产品集合起来,当需要多个产品同时协作时可以保证所有的产品生产的一致性(比如我们的月饼需要的原料),并隔离具体产品的创建
2):当有新产品族加入时,不许要修改代码,只需要添加一个具体的工厂
3):通过组合的方式创建对象,使用应用程序解耦,同时降低对特定对象 的依赖
缺点:
如果有产品等级加入,就需要修改工厂接口和所有的具体工厂