37

设计模式:工厂方法模式

 4 years ago
source link: https://chenjiabing666.github.io/2020/04/05/设计模式:工厂方法模式/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
  • 工厂方法模式是所有设计模式中比较常用的一种模式,但是真正能搞懂用好的少之又少,Spring底层大量的使用该设计模式来进行封装,以致开发者阅读源代码的时候晕头转向。
  • 今天陈某分别从以下五个方面详细讲述一下工厂方法模式:
    1. 从什么是工厂方法模式
    2. 通用框架实现
    3. 工厂方法模式的优点
    4. 工厂方法模式的升级
    5. Spring底层如何使用工厂方法模式

什么是工厂方法模式?

  • 定义:定义一个用于创建对象的 接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
  • 工厂方法模式通用类图如下:
    vuQfAbe.png!web
  • 在工厂方法模式中,抽象产品 Product 负责定义产品的特性,实现对事物的抽象定义。
  • AbstractFactory 是抽象工厂类,定义了一个抽象工厂方法。具体的如何创建产品由工厂实现类 ConcreteFactory 完成。

通用框架实现

  • 工厂方法模式的变种有很多,陈某给出一个比较实用的通用框架。
  • 抽象产品类

    public abstract class Product{
        /**
         * 公共逻辑方法
         */
        public void method1(){}
    
        /**
         * 抽象方法:由子类实现,根据业务逻辑定义多个
         */
        public abstract void method2();
    }
    
  • 具体产品类1 ,继承抽象产品类,如下:

    public class Product1extends Product{
        /**
         * 实现抽象产品类的抽象方法
         */
        @Override
        public void method2(){
    
        }
    }
    
  • 具体产品类2 ,继承抽象产品类,如下:

    public class Product2extends Product{
    
        /**
         * 实现抽象产品类的抽象方法
         */
        @Override
        public void method2(){
    
        }
    }
    
  • 抽象工厂类 ,必须定义一个工厂方法来自己实现具体的创建逻辑,如下:

    public abstract class AbstractFactory{
        /**
         * 工厂方法,需要子类实现
         * @param cls
         * @param <T>
         * @return
         */
        public abstract <T extends Product> Tcreate(Class<T> cls);
    }
    
  • 具体工厂类,使用了反射对具体产品的实例化,如下:

    public class ConcreteFactoryextends AbstractFactory{
        @Override
        public <T extends Product> Tcreate(Class<T> cls){
            Product product=null;
            try{
                product= (Product) Class.forName(cls.getName()).newInstance();
            }catch (Exception ex){
                ex.printStackTrace();
            }
            return (T) product;
        }
    }
    
  • 测试如下:

    public static void main(String[] args){
            //创建具体工厂类
            ConcreteFactory factory = new ConcreteFactory();
            //调用工厂方法获取产品类1的实例
            Product1 product1 = factory.create(Product1.class);
            System.out.println(product1);
        }
    
  • 以上是简单的一个通用框架,读者可以根据自己的业务在其上拓展。

工厂方法模式的优点

  • 良好的封装性,代码结构清晰,调用者不用关系具体的实现过程,只需要提供对应的产品类名称即可。
  • 易扩展性,在增加产品类的情况下,只需要适当的修改工厂类逻辑或者重新拓展一个工厂类即可。
  • 屏蔽了产品类,产品类的变化调用者不用关心。比如在使用 JDBC 连接数据库时,只需要改动一个驱动的名称,数据库就会从 Mysql 切换到 Oracle ,极其灵活。

工厂方法模式的升级

  • 在复杂的系统中,一个产品的初始化过程是及其复杂的,仅仅一个具体工厂实现可能有些吃力,此时最好的做法就是为每个产品实现一个工厂,达到一个工厂类只负责生产一个产品。
  • 此时工厂方法模式的类图如下:

JRvEZj6.png!web

  • 如上图,每个产品类都对应了一个工厂,一个工厂只负责生产一个产品,非常符合单一职责原则。
  • 针对上述的升级过程,那么工厂方法中不需要传入抽象产品类了,因为一个工厂只负责一个产品的生产,此时的抽象工厂类如下:
    public abstract class AbstractFactory{
        /**
         * 工厂方法,需要子类实现
         */
        public abstract <T extends Product> Tcreate();
    }
    

Spring底层如何使用工厂方法模式?

  • 工厂方法模式在Spring底层被广泛的使用,陈某今天举个最常用的例子就是 AbstractFactoryBean
  • 这个抽象工厂很熟悉了,这里不再讨论具体的作用。其实现了 FactoryBean 接口,这个接口中 getObject() 方法返回真正的Bean实例。
  • AbstractFactoryBean 中的 getObject() 方法如下:

    public final T getObject()throws Exception {
        //单例,从缓存中取,或者暴露一个早期实例解决循环引用
    		if (isSingleton()) {
    			return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
    		}
        //多实例
    		else { 
          //调用createInstance
    			return createInstance();
    		}
    	}
      //创建对象
      protected abstract T createInstance()throws Exception;
    
  • 从以上代码可以看出,创建对象的职责交给了 createInstance 这个抽象方法,由其子类去定制自己的创建逻辑。

  • 下图显示了继承了 AbstractFactoryBean 的具体工厂类,如下:

ae2qya2.png!web

  • 其实与其说 AbstractFactoryBean 是抽象工厂类,不如说 FactoryBean 是真正的抽象工厂类,前者只是对后者的一种增强,完成大部分的可复用的逻辑。比如常用的 sqlSessionFactoryBean 只是简单的实现了 FactoryBean ,并未继承 AbstractFactoryBean ,至于结论如何,具体看你从哪方面看了。

总结

  • 工厂方法模式是一种常见的设计模式,但是真正能够用的高级,用的透彻还是有些难度的,开发者所能做的就是在此模式基础上思考如何优化自己的代码,达到易扩展、封装性强的效果了。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK