概述
工厂方法模式的主要作用是将具体产品类的实例化延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化哪一个类。
问题由来
经过前面简单工厂的学习,我们知道该模式已经违背了开闭原则,一旦业务发生变更新增一个产品时,工厂类就不行了,我们需要修改其工厂类。所以我们可以通过工厂方法模式来解决这个问题。
之所以可以解决简单工厂的问题,是因为工厂方法模式把具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类,符合开放封闭原则,克服了简单工厂模式中缺点。
角色
- 抽象工厂(Creator)
该类为具体工厂的父类,主要用于规范描述具体工厂的公共接口
- 具体工厂(Concrete Creator)
被外界真正调用的类,实现工厂方法(
FactoryMethod
)创建产品的实例 - 抽象产品(Product)
该类为具体产品的父类,主要用于规范描述具体产品的公共接口
- 具体产品(Concrete Product)
工厂类创建的目标类,工厂创建出来的产品对象为该类的实例
操作步骤
- 创建抽象工厂类,定义具体工厂的公共接口;
- 创建抽象产品类 ,定义具体产品的公共接口;
- 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
- 创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
- 外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
案例分析
在前面简单工厂模式学习后,我们知道,女朋友喜欢喝红茶、绿茶,我们采用了工厂后非常愉快。感情也非常不错,但是突然有一天,女朋友想换一种口味了,想喝奶茶,但此时我们的工厂研磨机做不到了,此时怎么办呢?
此时肯定只能通过购买新的一台专门研磨奶茶的机器来解决
创建抽象工厂
abstract class TeaFactory { //抽象工厂,定义了返回产品的规范 public abstract Tea getTea(); }
创建抽象产品
abstract class Tea { abstract void drink(); abstract void drinkAfter(); }
创建具体产品
class RedTea extends Tea{ public RedTea(){ System.out.println("我在制作红茶"); } public void drink(){ System.out.println("喝红茶"); } public void drinkAfter(){ System.out.println("喝红茶之后,开始收拾"); } } class GreenTea extends Tea{ public GreenTea(){ System.out.println("我在制作绿茶"); } public void drink(){ System.out.println("喝绿茶"); } public void drinkAfter(){ System.out.println("喝绿茶之后,开始收拾"); } } class MilkTea extends Tea{ public MilkTea(){ System.out.println("我在制作奶茶"); } public void drink(){ System.out.println("喝奶茶"); } public void drinkAfter(){ System.out.println("喝奶茶之后,开始收拾"); } }
创建具体工厂
//红茶生产工厂 class RedTeaFactory extends TeaFactory{ @Override public Tea getTea() { return new RedTea(); } } //果汁生产工厂 class GreenTeaFactory extends TeaFactory{ @Override public Tea getTea() { return new GreenTea(); } } //奶茶生产 class MilkTeaFactory extends TeaFactory{ @Override public Tea getTea() { return new MilkTea(); } }
调用层
public class Client { public static void main(String[] args) { //喝奶茶,需要奶茶研磨机进行研磨 TeaFactory milkTeaFactory = new MilkTeaFactory(); //得到奶茶对象 Tea tea = milkTeaFactory.getTea(); tea.drink(); tea.drinkAfter(); } }
优缺点
优点
- 更符合开-闭原则,新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
- 符合单一职责原则,每个具体工厂类只负责创建对应的产品
- 不使用静态工厂方法,可以形成基于继承的等级结构
缺点
- 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
- 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
- 一个具体工厂只能创建一种具体产品
应用场景
- 当一个类不知道它所需要的对象的类时
在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可;
- 当一个类希望通过其子类来指定创建对象时
在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。 - 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
总结
工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。