工厂方法模式(Factory method pattern)
定义
工厂方法模式(英语:Factory method pattern)是一种实现了"工厂"概念的面向对象设计模式。就像其他创建型模式一样,它也是处理在不指定对象具体类型的情况下创建对象的问题。工厂方法模式的实质是"定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。
类图
从上图可以看出4种类,抽象工厂,具体工厂,抽象产品,具体产品;回忆下简单工厂,貌似比他多了一个抽象工厂对吧。下面我们用列出java代码实现来看看他和简单工厂的区别。
java代码实现
抽象产品
public interface Product {
void method();
}
具体产品
public class ProductA implements Product {
@Override
public void method() {
System.out.println("productA 的方法");
}
}
抽象工厂
public interface Factory {
Product createProduct();
}
具体工厂
public class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ProductA();
}
}
那么我们有新的产品的时候只需要新增一个具体的工厂类,一个具体的产品类 例如:ConcreteFactoryB``ProductB
来生产相应的产品。这样就省去了简单工厂中的if else判断,解决了简单工厂违背开闭原则的诟病。下面我们还是举出一个实例来说明。
实例一,JDBC API
废话不多我们直接看类图 我们可以看到,除了多了一个DriverManager
其他于我们工厂方法的类图一模一样。Driver
就是抽象工厂,而我们的数据库厂商会提供各自的驱动实现,Connection
就是我们的抽象产品,同样数据库厂商会为我们提供各自的实现类,例如mysql的实现类com.mysql.jdbc.ConnectionImpl
,oracle也会提供对应的实现类。下面看一看Driver
接口部分源码:
public interface Driver {
Connection connect(String url, java.util.Properties info)
throws SQLException;
}
这个方法connect
就是创建连接的方法定义,具体的实现都由数据库厂商实现好了,我们不用关心。接着在看一看Connection
接口:
public interface Connection extends Wrapper, AutoCloseable {
Statement createStatement() throws SQLException;
PreparedStatement prepareStatement(String sql)
throws SQLException;
}
同样声明了很多我们平时使用的方法(创建静态,动态块执行我们的sql),具体实现都由不同的厂商提供。那么问题来了,居然Driver
和Connection
就能搞定为什么还要一个DriverManager
呢? DriverManager
为我们解决了驱动间切换的问题,我们平时使用的时候直接Class.forName("driverClass")
,然后DriverManager.getConnection("url","name","pwd")
就获取到了连接。 按理说我们应该new Driver().connect()
,针对不同的数据库创建不同的驱动对象。但是DriverManager帮我们屏蔽了这些操作,我们来看看具体是怎么实现的吧。
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
//注册驱动到DriverManager缓存
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
这里是mysql的驱动类,我们调用Class.forName()
的时候就吧驱动注册到了DriverManager
,再来看看DriverManager
怎么获取链接的:
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
这是我们最熟悉的方法,好像还没到关键点。继续往下,直接看最关键的地方
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException
//下面是这个方法的代码片段:
for(DriverInfo aDriver : registeredDrivers) {
// 如果类加载器没法加载当前这个驱动,跳过继续往下。 从上面我们看到这个加载器是调用我们DriverManager的类的。
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
//判断是否有权限加载驱动
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
boolean result = false;
if (driver != null) {
Class<?> aClass = null;
try {
aClass = Class.forName(driver.getClass().getName(), true, classLoader);
} catch (Exception ex) {
result = false;
}
result = (aClass == driver.getClass()) ? true : false;
}
return result;
}
到这里我们明白了为啥么需要DriverManager
了。
总结
设计原则:遵循单一职责、依赖倒置、开闭原则
常用场景:一种场景是希望工厂与产品的种类对客户端保持透明,给客户端提供一致的操作,另外一种是不同的工厂和产品可以提供客户端不同的服务或功能
使用概率:60%
复杂度:中低
变化点:工厂与产品的种类
选择关键点:工厂类和产品类是否是同生同灭的关系
逆鳞:无
相关设计模式
抽象工厂模式:工厂方法模式与抽象工厂模式最大的区别在于,在工厂方法模式中,工厂创造的是一个产品,而在抽象工厂模式中,工厂创造的是一个产品族。