23种设计模式-工厂方法模式

时间:2022-10-02 11:45:27

1.产生背景

  • 为什么需要工厂方法

上一节我们了解了简单工厂,它实现了创建一个抽象(接口、类)多实例的解决方案,基本满足了对象的创建,那么为什么还需要工厂方法模式?

任何事物的产生,一定有它的原因,简单来说,一定是它解决了前人未解决的某个问题或者缺陷;

现在来看看简单工厂有哪些缺陷:

    pulic interface SimpleInf{
        void doSomething();
    }

    public class SimpleObj_1 implements SimpleInf{
        public void doSomething(){
            xxxxxx
        }
    }

    public class SimpleObj_2 implements SimpleInf{
        public void doSomething(){
            xxxxxx
        }
    }

    public class SimpleFactory{
        public static SimpleInf createObj(int type){
            if(type == 1){
                return new SimpleObj_1();
            }else if(type == 2){
                return new SimpleObj_2();
            }else{
                return null;
            }
        }
    }

    public class Client{
        public void myMethod(){
            SimpleInf  obj = SimpleFactory.createObj(1);
            obj.doSomething();
        }
    }

从上面代码可以看出简单工厂有以下特点:

  • 可以创建一个抽像的一系列对象
  • 可以创建一个具体类的多个对象(调用一次创建一个)
  • 客户使用简单(判断逻辑与初始化逻辑,全封装在工厂内)
  • 静态方法(具有工具类性质)

从以上的特点可以分析出,它有一个重要缺陷:

难以扩展

  • 静态方法:
    无法重载,因此也无法通过继承或实现的方式扩展;
  • 判断逻辑下沉:
    虽然把复杂逻辑抽象到工厂内,方便外部使用,但是把判断逻辑下沉到底层代码,导致工厂逻辑复杂度上升,每次新增一个类,就必须修改工厂,违背了“开闭原则”

2.概念

一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类

概念过于晦涩,暂且不用理会;

3.目的

解决简单工厂难以扩展的缺点

4.解决方案

本质:把简单工厂中,if else 部分抽像到具体工厂类;每个工厂只创建一个具体类的实例;以方便以后扩展;

也即:封装类中不变的部分,提取其中个性化善变的部分为独立类(策略化)

  • 去除静态方法
  • 工厂抽象化
  • 判断逻辑策略化

通过上面的分析,我们知道简单工厂难以扩展最重要的原因是:工厂中的方法不能重载、判断逻辑下沉; 因此,只要解决了这两个问题就可以了;

重载:通过类继承或接口化,同时去除静态方法,就可以实现;
策略化:把一个个If - else判断的代码块,拆分到工厂实现类中,每个工厂只负责一个类的实例创建;
判断逻辑上浮: 把if - else上升到客户,由客户来做判断;
23种设计模式-工厂方法模式

通过上图,我们把简单工厂改造成了工厂方法模式,把if -else抽像成一个个策略,并迁移到每个具体工厂类,可是if else 应该在什么时间判断,或者客户怎么使用呢?

    public class client{
        public void doSomethind(int type){
            //判断逻辑返还给客户
            MethodFactory  factory = null;
            if(type == 1){
                factory = new MethodFactory_1();
            }else if(type == 2){
                factory = new MethodFactory_2();
            }
            //目标实例,仍然由工厂创建
            SimpleInf  obj =factory.createObj();
            obj.doSomething();
        }
    }

5. 类图

23种设计模式-工厂方法模式

6.优缺点

优点:

无论它有什么缺点,暂且放下,先来看看其中的优点:

  • 判断逻辑上浮(到客户层),简化工厂逻辑
  • 判断逻辑策略化,每个工厂只负责一个具体的实例创建,职责单一
  • 判断逻辑策略化,扩展性强,每增加一个具体类,只需要实现对应的工厂即要,不需要修改原来的工厂
  • 工厂抽象化(接口、抽像类),对外提供统一操作入口

最核心的目的:就是增强了工厂的可扩展性;

缺点:

看到上面的代码,是否发现,判断逻辑的上浮,又回到了“简单工厂模式”之前的状态;推翻了“简单工厂模式”,也推翻了它带来的优点和好处;

同时,我们提到了策略化,那么它跟策略模式又有什么区别;

  • 客户使用复杂化
    原本使用“简单工厂”封装对象创建的逻辑判断,现在客户创建对象,必须先判断并选择性创建对应的工厂,然后再使用此工厂创建对应的业务对象;

  • 与策略模式区别
    这个称不上缺点,既然提到了“策略”,就对比一下他们之间的区别;
    本质上,他们的实现方式没有任何区别,只是目的不同;

工厂方法的目的是:创建对象;重在对象的创建、初始化、装配;

策略模式的目的是:封装逻辑;重在逻辑,可以有返回值,也可以没有;

解释
工厂方法模式,确实推翻了简单工厂模式;因为每种解决方案都不会十全十美,一定有所取舍;因此工厂方法模式在 “使用方便”“扩展性” 之间,倾向了后者;

在我们的例子中,对象的创建逻辑都非常简单,所以对比不出不同模式的优点,而现实中,正是因为对象的创建过于复杂,才使用工厂,因此面对复杂的创建逻辑,客户的方便性的重要性要低很多;

同时,在实际编程中,可以通过IOC等方式解决客户的使用过程中的逻辑判断;

7.现实场景

spring框架的中BeanFactory是使用工厂方法模式的最佳案例;
通过BeanFactory接口,抽像对外统一入口;
通过ConfigurableBeanFactory、ClassPathXmlApplicationContext等各种实现类,扩展了不同场景下的具体工厂;