前言
上一篇文章我们讲了简单工厂模式,拿了商店提供产品做例子,我们向商店说明要什么产品,商店就提供我们什么产品。
使用简单工厂模式时,实现的商店(工厂类)是这样的。
/**
* 洗护产品店
*
* @author dashu
* @since 2021/8/2
*/
public class HygieneProductStore {
public static HygieneProduct offer(HygieneProductEnum hygieneProduct) {
if (HygieneProductEnum.SHAMPOO.equals(hygieneProduct)) {
return new Shampoo();
} else if (HygieneProductEnum.BODY_WASH.equals(hygieneProduct)){
return new BodyWash();
} else {
throw new UnsupportedOperationException("本店暂不卖这类产品");
}
}
}
这里商店的offer方法,我们指定洗发水,商店就创建洗发水给我们,我们指定沐浴露,商店就创建沐浴露对象给我们。
简单工厂模式基本实现了将对象的生产和使用分离。
但是这里有一个缺陷。目前商店只提供洗发水和沐浴露,如果某一天,商店准备增供
【防晒霜】呢?
那就加多一个【防晒霜】的具体产品类,并且在商店的offer方法中多加一个else,创建【防晒霜】对象。
如果之后又添加【洗手液】【护发素】等等洗护产品呢,当然,可以继续新建产品类,继续往商店的offer方法中添加else,这样做是可以实现我们的功能的。
但是如果这些产品经常变动,经常增加或者删减,你会发现,使用简单工厂模式的话,就得反复修改工厂类。每变动一次,我们就不得不修改工厂类一次。
这样子频繁修改同一个类的一个方法,是很容易改出问题的。我们的示例还是比较简单的,直接就new了,如果工厂生产对象的逻辑比较复杂呢。
软件设计中有一个“开闭原则”,对拓展开放,对修改关闭,这种反复修改offer方法的行为就违背了开闭原则。
如果我们的产品的种类基本不变或者很少改变,那么简单工厂模式是够用了,但是如果产品种类经常变化,那么我们应该引入工厂方法模式 ---- 简单工厂模式的升级版。
1. 定义
工厂方法模式又称为工厂模式,虚拟构造器模式或者多态工厂模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,工厂子类负责创建具体产品,将产品类的实例化延迟到了工厂子类中完成。
2. 模式结构
组成
- 抽象产品
- 具体产品
- 抽象工厂
- 具体工厂
工厂方法模式跟简单工厂模式对比,工厂类有了一个抽象的工厂。
3. 代码示例
工厂方法模式实现洗护产品店卖洗护产品是这样子的。
- 有一个抽象的产品 ---- 洗护产品。
- 有一些具体的产品类 ---- 洗发水、沐浴露。
- 有一个抽象工厂,定义了一个offer(提供产品)方法,实现逻辑是,你想要洗发水,我就让洗发水专区提供一瓶洗发水给你,你想要沐浴露,我就让沐浴露专区提供沐浴露给你。
- 还有一些具体的工厂继承自抽象工厂,比如洗发水专区(具体工厂),沐浴露专区(具体工厂)。
如果我之后想要加入【防晒霜】产品,那么就新建一个【防晒霜】类(新的具体产品),新建一个【防晒霜】专区(新的具体工厂)。
如果我之后想要移除【洗发水】产品,那么就删掉【洗发水】类(已有的具体产品),删除【洗发水】专区(已有的具体工厂)。
看,这样子,但是无论新增产品还是删除产品,都不会动到抽象工厂生产产品的逻辑。
新增产品时,只需要关注新产品,删除产品时,只需要关注删除的产品,不用担心影响到其他的产品。
------------------------------------------------------代码示例----------------------------------------------------------
抽象产品
/**
* 洗护产品
*
* @author dashu
* @since 2021/8/20
*/
public abstract class HygieneProduct {
public abstract void whoAmI();
}
具体产品
/**
* 洗发水
*
* @author dashu
* @since 2021/8/20
*/
public class Shampoo extends HygieneProduct {
public void whoAmI() {
System.out.println("我是洗发水");
}
}
/**
* 沐浴露
*
* @author dashu
* @since 2021/8/20
*/
public class BodyWash extends HygieneProduct {
public void whoAmI() {
System.out.println("我是沐浴露");
}
}
抽象工厂
/**
* 洗护产品店
*
* @author dashu
* @since 2021/8/23
*/
public abstract class HygieneProductStore {
public abstract HygieneProduct offer();
}
具体工厂
/**
* 洗发水专区
*
* @author dashu
* @since 2021/8/23
*/
public class ShampooArea extends HygieneProductStore {
@Override
public HygieneProduct offer() {
return new Shampoo();
}
}
/**
* 沐浴露专区
*
* @author dashu
* @since 2021/8/23
*/
public class BodyWashArea extends HygieneProductStore {
@Override
public HygieneProduct offer() {
return new BodyWash();
}
}
客户端
/**
* 客户端
*
* @author dashu
* @since 2021/8/23
*/
public class Client {
public static void main(String[] args) {
ShampooArea shampooArea = new ShampooArea();
HygieneProduct shampoo = shampooArea.offer();
shampoo.whoAmI();
BodyWashArea bodyWashArea = new BodyWashArea();
HygieneProduct bodyWash = bodyWashArea.offer();
bodyWash.whoAmI();
}
}
4. 模式优缺点
优点
- 工厂方法用来创建客户端需要的产品,又向客户端隐藏了具体产品类实例化的细节。用户只需要关心产品对应的工厂即可,无需关心具体产品类名和创建细节。
- 工厂可以自主确定创建什么产品,如何创建的细节都是完全封装在具体工厂内部的。
- 在加入新产品时,无需修改客户端,无需修改抽象工厂,也无需修改其他的具体工厂和具体产品,只需要增加一个新的具体工厂和具体产品。
缺点
- 需要增加一个产品时,就要新增一个对应的工厂,每次新增都是成对新增,一定程度增加了系统的复杂度。
- 引入了抽象层,增加了系统的抽象性和理解难度。