摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单)。同时介绍了简单工厂模式(未)遵循的OOP原则。最后给出了简单工厂模式在JDBC中的应用java
原创文章。同步自做者我的博客http://www.jasongj.com/design_pattern/simple_factorygit
有一种抽象产品——汽车(Car),同时有多种具体的子类产品,如BenzCar,BMWCar,LandRoverCar。类图以下
github
做为司机,若是要开其中一种车,好比BenzCar,最直接的作法是直接建立BenzCar的实例,并执行其drive方法,以下sql
package com.jasongj.client; import com.jasongj.product.BenzCar; public class Driver1 { public static void main(String[] args) { BenzCar car = new BenzCar(); car.drive(); } }
此时若是要改成开Land Rover,则须要修改代码,建立Land Rover的实例并执行其drive方法。这也就意味着任什么时候候须要换一辆车开的时候,都必须修改客户端代码。数据库
一种稍微好点的方法是,经过读取配置文件,获取须要开的车,而后建立相应的实例并由父类Car的引用指向它,利用多态执行不一样车的drive方法。以下apache
package com.jasongj.client; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jasongj.product.BMWCar; import com.jasongj.product.BenzCar; import com.jasongj.product.Car; import com.jasongj.product.LandRoverCar; public class Driver2 { private static final Logger LOG = LoggerFactory.getLogger(Driver2.class); public static void main(String[] args) throws ConfigurationException { XMLConfiguration config = new XMLConfiguration("car.xml"); String name = config.getString("driver2.name"); Car car; switch (name) { case "Land Rover": car = new LandRoverCar(); break; case "BMW": car = new BMWCar(); break; case "Benz": car = new BenzCar(); break; default: car = null; break; } LOG.info("Created car name is {}", name); car.drive(); } }
对于Car的使用方而言,只须要经过参数便可指定所须要Car的各种并获得其实例,同时不管使用哪一种Car,都不须要修改后续对Car的操做。至此,简单工厂模式的原型已经造成。若是把上述的逻辑判断封装到一个专门的类的静态方法中,则实现了简单工厂模式。工厂代码以下设计模式
package com.jasongj.factory; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jasongj.product.BMWCar; import com.jasongj.product.BenzCar; import com.jasongj.product.Car; import com.jasongj.product.LandRoverCar; public class CarFactory1 { private static final Logger LOG = LoggerFactory.getLogger(CarFactory1.class); public static Car newCar() { Car car = null; String name = null; try { XMLConfiguration config = new XMLConfiguration("car.xml"); name = config.getString("factory1.name"); } catch (ConfigurationException ex) { LOG.error("parse xml configuration file failed", ex); } switch (name) { case "Land Rover": car = new LandRoverCar(); break; case "BMW": car = new BMWCar(); break; case "Benz": car = new BenzCar(); break; default: car = null; break; } LOG.info("Created car name is {}", name); return car; } }
调用方代码以下工具
package com.jasongj.client; import com.jasongj.factory.CarFactory1; import com.jasongj.product.Car; public class Driver3 { public static void main(String[] args) { Car car = CarFactory1.newCar(); car.drive(); } }
与Driver2相比,全部的判断逻辑都封装在工厂(CarFactory1)当中,Driver3再也不须要关心Car的实例化,实现了对象的建立和使用的隔离。oop
固然,简单工厂模式并不要求必定要读配置文件来决定实例化哪一个类,能够把参数做为工厂静态方法的参数传入。优化
从Driver2和CarFactory1的实现中能够看到,当有新的车加入时,须要更新Driver2和CarFactory1的代码也实现对新车的支持。这就违反了开闭原则
(Open-Close Principle)。能够利用反射(Reflection)解决该问题。
package com.jasongj.factory; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jasongj.product.Car; public class CarFactory2 { private static final Logger LOG = LoggerFactory.getLogger(CarFactory2.class); public static Car newCar() { Car car = null; String name = null; try { XMLConfiguration config = new XMLConfiguration("car.xml"); name = config.getString("factory2.class"); } catch (ConfigurationException ex) { LOG.error("Parsing xml configuration file failed", ex); } try { car = (Car)Class.forName(name).newInstance(); LOG.info("Created car class name is {}", name); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { LOG.error("Instantiate car {} failed", name); } return car; } }
从上面代码中能够看到,以后若是须要引入新的Car,只须要在配置文件中指定该Car的完整类名(包括package名),CarFactory2便可经过反射将其实例化。实现了对扩展的开放,同时保证了对修改的关闭。熟悉Spring的读者应该会想到Spring IoC的实现。
上例中使用反射作到了对扩展开放,对修改关闭。但有些时候,使用类的全名不太方便,使用别名会更合适。例如Spring中每一个Bean都会有个ID,引用Bean时也会经过ID去引用。像Apache Nifi这样的数据流工具,在流程上使用了职责链模式,而对于单个Processor的建立则使用了工厂,对于用户自定义的Processor并不须要经过代码去注册,而是使用注解(为了更方便理解下面这段代码,请先阅读笔者另一篇文章《Java系列(一)Annotation(注解)》)。
下面就继续在上文案例的基础上使用注解升级简单工厂模式。
package com.jasongj.factory; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.reflections.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jasongj.annotation.Vehicle; import com.jasongj.product.Car; public class CarFactory3 { private static final Logger LOG = LoggerFactory.getLogger(CarFactory3.class); private static Map<String, Class> allCars; static { Reflections reflections = new Reflections("com.jasongj.product"); Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(Vehicle.class); allCars = new ConcurrentHashMap<String, Class>(); for (Class<?> classObject : annotatedClasses) { Vehicle vehicle = (Vehicle) classObject.getAnnotation(Vehicle.class); allCars.put(vehicle.type(), classObject); } allCars = Collections.unmodifiableMap(allCars); } public static Car newCar() { Car car = null; String type = null; try { XMLConfiguration config = new XMLConfiguration("car.xml"); type = config.getString("factory3.type"); LOG.info("car type is {}", type); } catch (ConfigurationException ex) { LOG.error("Parsing xml configuration file failed", ex); } if (allCars.containsKey(type)) { LOG.info("created car type is {}", type); try { car = (Car) allCars.get(type).newInstance(); } catch (InstantiationException | IllegalAccessException ex) { LOG.error("Instantiate car failed", ex); } } else { LOG.error("specified car type {} does not exist", type); } return car; } }
从上面代码中能够看到,该工厂会扫描全部被Vehicle注解的Car(每种Car都在注解中声明了本身的type,可做为该种Car的别名)而后创建起Car别名与具体Car的Class原映射。此时工厂的静态方法便可根据目标别名实例化对应的Car。
本文全部代码均可从做者GitHub下载.
简单工厂模式(Simple Factory Pattern)又叫静态工厂方法模式(Static FactoryMethod Pattern)。专门定义一个类(如上文中的CarFactory一、CarFactory二、CarFactory3)来负责建立其它类的实例,由它来决定实例化哪一个具体类,从而避免了在客户端代码中显式指定,实现了解耦。该类因为能够建立同一抽象类(或接口)下的不一样子类对象,就像一个工厂同样,所以被称为工厂类。
简单工厂模式类图以下所示
简单工厂模式在JDK中最典型的应用要数JDBC了。能够把关系型数据库认为是一种抽象产品,各厂商提供的具体关系型数据库(MySQL,PostgreSQL,Oracle)则是具体产品。DriverManager是工厂类。应用程序经过JDBC接口使用关系型数据库时,并不须要关心具体使用的是哪一种数据库,而直接使用DriverManager的静态方法去获得该数据库的Connection。
package com.jasongj.client; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JDBC { private static final Logger LOG = LoggerFactory.getLogger(JDBC.class); public static void main(String[] args) { Connection conn = null; try { Class.forName("org.apache.hive.jdbc.HiveDriver"); conn = DriverManager.getConnection("jdbc:hive2://127.0.0.1:10000/default"); PreparedStatement ps = conn.prepareStatement("select count(*) from test.test"); ps.execute(); } catch (SQLException ex) { LOG.warn("Execute query failed", ex); } catch(ClassNotFoundException e) { LOG.warn("Load Hive driver failed", e); } finally { if(conn != null ){ try { conn.close(); } catch (SQLException e) { // NO-OPT } } } } }