设计模式-挖掘工厂模式应用场景-全面分析工厂模式原理

工厂模式的应用场景很多,到底再那些地方用过呢?为什么需要使用?有什么好处?本文将从应用场景出发逐步揭开工厂模式背后的面纱。

先对工厂模式做一个大概的说明,其定义:

Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)

有了一个大概的概念后,我们来列举一下其应用场景:

  • Spring获取Bean,通过BeanFacotory工厂接口实现;
  • Log日志,通过LoggerFactory获取日志对象;
  • jdbc,创建数据库连接;

 Spring获取bean的代码如下:

# 通过xml获取Bean
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-factoryMethod.xml");
DemoApplication bean = ctx.getBean(DemoApplication.class);

# 通过DI的方式注入Bean
@Autowired
DemoApplication demoApplication;

# 通过DI的方式注入Bean
@Service
DemoApplication demoApplication;

接下来我们看看ClassPathXmlApplicationContext是怎么实现的,先看到下面的类结构图:

BeanFactory接口存在四种工厂SimpleJndiBeanFactory、HierachicalBeanFacory、AutowireCapleBeanFactory、ListableBeanFactory,这四个工厂也对应了获取Bean的不同方式比如通过Jndi获取bean就使用SimpleJndiBeanFactory工厂,通过xml获取bean就通过HierachicalBeanFacory工厂下的实现,通过依赖注入的方式就通过AutowireCapleBeanFactory工厂。

Spring中有一个最大的功能就是DI(依赖注入),所依赖的对象一般都是接口,这也吻合了设计原则中的依赖倒置原则(DIP),而创建bean与之相关的也都交给了Spring容器来管理,也就是不需要关注bean的创建过程以及与之相关的其他对象,也就是设计原则中的迪米特法则(LOD),在这里我们所有的接口也可以使用其子类来进行替换,这也就吻合里氏替换原则(LSP)。

Spring创建bean的使用的设计模式即为抽象工厂模式,需要实现不同创建bean方式只需要继承BeanFactory接口进行实现。

抽象工厂定义:

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

在最早学习Java的时候,JDBC获取数据库连接,实现代码如下:


import java.sql.Connection;
import java.sql.SQLException;
 
public class ConnectionFactory {
 
	public static Connection getConnection() {
		Connection conn = null;
 
			try {
				conn = DataSourceHolder.getInstance().getDataSource()
						.getConnection();
			} catch (SQLException e) {
				e.printStackTrace();
			}
 
		return conn;
	}

}


import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
 
 
public class DataSourceHolder {
	private BasicDataSource ds = new BasicDataSource();
 
	private DataSourceHolder() {
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost:3306/dbo");//dbo为连接的数据库名称
		ds.setUsername("root");//账号
		ds.setPassword("root");//密码
	}
	private static class SingletonHolder{
		private static DataSourceHolder instance = new DataSourceHolder();
	}
	public static DataSourceHolder getInstance(){
		return SingletonHolder.instance;
	}
	public DataSource getDataSource(){
		return ds;
	}

}

由于获取数据连接需要比较多的参数设置,其过程还是相对比较复制,因此使用ConnectionFactory数据库连接的工厂,非常简单的就可以获取数据库连接对象(使用ConnectionFactory .getConnection())。

以上这种使用静态方法获取数据库连接的方式就是静态工厂模式,看到这种方式是不是可以想到我们之前实现的很多代码都非常相似,比如时间工具类、Json工具类,其实这些都归纳为静态工厂或者称之为简单工厂。

简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

接下来我们看看Logger的实现原理:

获取logger的对象通常会通过以下方式获取

org.slf4j.Logger logger = LoggerFactory.getLogger("DemoApplication");

查询getLogger源码:

public final class LoggerFactory {

    public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }


    public static ILoggerFactory getILoggerFactory() {
        if (INITIALIZATION_STATE == UNINITIALIZED) {
            synchronized (LoggerFactory.class) {
                if (INITIALIZATION_STATE == UNINITIALIZED) {
                    INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                    performInitialization();
                }
            }
        }
        switch (INITIALIZATION_STATE) {
        case SUCCESSFUL_INITIALIZATION:
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
        case NOP_FALLBACK_INITIALIZATION:
            return NOP_FALLBACK_FACTORY;
        case FAILED_INITIALIZATION:
            throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
        case ONGOING_INITIALIZATION:
            // support re-entrant behavior.
            // See also http://jira.qos.ch/browse/SLF4J-97
            return SUBST_FACTORY;
        }
        throw new IllegalStateException("Unreachable code");
    }
}

很容易就可以判别出Logger使用了静态工厂,我们可以看到getILoggerFactory()方法,在这里根据logger不同配置参数创建了不同工厂,而这里所有的工厂都是实现了ILoggerFactory接口,因此在日志这个组件中也是用了抽象工厂,因此我们可以看出不管是Spring或者日志组件都使用了抽象工厂来增强其扩展性,这也就体现了抽象工厂最大的特性就是可扩展性强,也吻合开闭原则对扩展开发对修改关闭。

接下来我们看看这样的一段代码:

产品 

public abstract class Product {      
     //产品类的公共方法
     public void method1(){
             //业务逻辑处理
     }  
     //抽象方法
     public abstract void method2();    
}

public class ConcreteProduct1 extends Product {
     public void method2() {
             //业务逻辑处理
     }
}

public class ConcreteProduct2 extends Product {
     public void method2() {
             //业务逻辑处理
     }
}

抽象工厂

public abstract class Creator {     
     /*
      * 创建一个产品对象,其输入参数类型可以自行设置
      * 通常为String、Enum、Class等,当然也可以为空
      */        
     public abstract <T extends Product> T createProduct(Class<T> c);
}

public class ConcreteCreator extends Creator {      
     public <T extends Product> T createProduct(Class<T> c){
             Product product=null;
             try {
                    product = (Product)Class.forName(c.getName()).newInstance();
             } catch (Exception e) {
                    //异常处理
             }
             return (T)product;         
     }
}

场景类

public class Client {
     public static void main(String[] args) {
             Creator creator = new ConcreteCreator();
             Product product = creator.createProduct(ConcreteProduct1.class);
             /*
              * 继续业务处理
              */
     }
}

以上是通过产品名称创建对应的产品的过程,这也是很多资料中介绍工厂模式时所使用的示例,这也就时工厂方法模式最基本的实现。

总结:

(1)简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory Method Pattern),一般使用静态方法进行实现。
(2)工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式,Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。);
(3)抽象工厂(Abstract Factory)模式,Provide an interface for creating families of related or dependent objects without specifying their concrete classes.(为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类。)。

优点:

  1. 首先,良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。
  2. 其次,工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。例如在我们的例子中,需要增加一个棕色人种,则只需要增加一个BrownHuman类,工厂类不用任何修改就可完成系统扩展。
  3. 再次,屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。因为产品类的实例化工作是由工厂类负责的,一个产品对象具体由哪一个产品生成是由工厂类决定的。在数据库开发中,大家应该能够深刻体会到工厂方法模式的好处:如果使用JDBC连接数据库,数据库从MySQL切换到Oracle,需要改动的地方就是切换一下驱动名称(前提条件是SQL语句是标准语句),其他的都不需要修改,这是工厂方法模式灵活性的一个直接案例。
  4. 最后,工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类,没问题!

使用场景:

  1. 首先,工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。
  2. 其次,需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。万物皆对象,那万物也就皆产品类,例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择:POP3、IMAP、HTTP,我们就可以把这三种连接方法作为产品类,定义一个接口如IConnectMail,然后定义对邮件的操作方法,用不同的方法实现三个具体的产品类(也就是连接方式)再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式。如此设计,可以做到完美的扩展,如某些邮件服务器提供了WebService接口,很好,我们只要增加一个产品类就可以了。
  3. 再次,工厂方法模式可以用在异构项目中,例如通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际的开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题,等等。从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。
  4. 最后,可以使用在测试驱动开发的框架下。例如,测试一个类A,就需要把与类A有关联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B的耦合。目前由于JMock和EasyMock的诞生,该使用场景已经弱化了,读者可以在遇到此种情况时直接考虑使用JMock或EasyMock。

笔者的微信公众号,每天一篇好文章:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值