分类:01_设计模式
工厂方法模式 Factory Method pattern
,属于创建型模式.
概念: 定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
工厂方法模式的目的很简单,就是封装对象创建的过程,提升创建对象方法的可复用性。
工厂方法模式的主要角色:
抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
我们直接来看看工厂方法模式的 UML 图:
为了提高代码扩展性,我们需要将简单工厂中的if分支逻辑去掉,通过增加抽象工厂(生产工厂的工厂)的方式,让具体工厂去进行实现,由具体工厂来决定实例化哪一个具体的产品对象.
抽象工厂
public interface FreeGoodsFactory {
IFreeGoods getInstance();
}
具体工厂
public class DiscountFreeGoodsFactory implements FreeGoodsFactory {
@Override
public IFreeGoods getInstance() {
return new DiscountFreeGoods();
}
}
public class SmallGiftFreeGoodsFactory implements FreeGoodsFactory {
@Override
public IFreeGoods getInstance() {
return new SmallGiftFreeGoods();
}
}
Controller
public class DeliverController {
/**
* 按照类型的不同发放商品
*/
public ResponseResult awardToUser(AwardInfo awardInfo){
FreeGoodsFactory freeGoodsFactory = null;
if(awardInfo.getAwardType() == 1){
freeGoodsFactory = new DiscountFreeGoodsFactory();
}else if(awardInfo.getAwardType() == 2){
freeGoodsFactory = new SmallGiftFreeGoodsFactory();
}
IFreeGoods freeGoods = freeGoodsFactory.getInstance();
System.out.println("=====工厂方法模式========");
ResponseResult result = freeGoods.sendFreeGoods(awardInfo);
return result;
}
}
从上面的代码实现来看,工厂类对象的创建逻辑又耦合进了 awardToUser() 方法中,跟我们最初的代码版本非常相似,引入工厂方法非但没有解决问题,反倒让设计变得更加复杂了。
那怎么 来解决这个问题呢?
我们可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象。
/**
* 用简单方法模式实现: 工厂的工厂,作用是不需要每次创建新的工厂对象
* @author spikeCong
* @date 2022/9/9
**/
public class FreeGoodsFactoryMap {
private static final Map<Integer,FreeGoodsFactory> cachedFactories = new HashMap<>();
static{
cachedFactories.put(1, new DiscountFreeGoodsFactory());
cachedFactories.put(2, new SmallGiftFreeGoodsFactory());
}
public static FreeGoodsFactory getParserFactory(Integer type){
if(type == 1){
FreeGoodsFactory freeGoodsFactory = cachedFactories.get(1);
return freeGoodsFactory;
}else if(type ==2){
FreeGoodsFactory freeGoodsFactory = cachedFactories.get(2);
return freeGoodsFactory;
}
return null;
}
}
Controller
/**
* 发放奖品接口
* @author spikeCong
* @date 2022/9/7
**/
public class DeliverController {
/**
* 按照类型的不同发放商品
*/
public ResponseResult awardToUser(AwardInfo awardInfo){
//根据类型获取工厂
FreeGoodsFactory goodsFactory = FreeGoodsFactoryMap.getParserFactory(awardInfo.getAwardType());
//从工厂中获取对应实例
IFreeGoods freeGoods = goodsFactory.getInstance();
System.out.println("=====工厂方法模式========");
ResponseResult result = freeGoods.sendFreeGoods(awardInfo);
return result;
}
}
现在我们的代码已经基本上符合了开闭原则,当有新增的产品时,我们需要做的事情包括:
创建新的产品类,并且让该产品实现抽象产品接口
创建产品类对应的具体工厂,并让具体工厂实现抽象工厂
将新的具体工厂对象,添加到FreeGoodsFactoryMap的cachedFactories中即可,需要改动的代码改动的非常少.
工厂方法模优缺点
优点:
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
缺点:
每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
什么时候使用工厂方法模式
需要使用很多重复代码创建对象时,比如,DAO 层的数据对象、API 层的 VO 对象等。
创建对象要访问外部信息或资源时,比如,读取数据库字段,获取访问授权 token 信息,配置文件等。
创建需要统一管理生命周期的对象时,比如,会话信息、用户网页浏览轨迹对象等。
创建池化对象时,比如,连接池对象、线程池对象、日志对象等。这些对象的特性是:有限、可重用,使用工厂方法模式可以有效节约资源。
希望隐藏对象的真实类型时,比如,不希望使用者知道对象的真实构造函数参数等。