这个设计模式是由简单的简单工厂模式抽象化而来,怎么说呢,就是把工厂抽象成一个接口,具体的生产方法放到具体的工厂实现类里实现,这样就满足了开闭原则的对修改关闭,对拓展开发了。
定义:工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
以上定义与类图来自百度百科
由类图可以看出现在的工厂是一个接口,下面由各个具体工厂的实现分别负责生产不同的产品,让客户端决定你要使用哪一个工厂,进而生产对应的产品。下面上代码。
首先是工厂接口
package test;
public interface Factory {
public Product createProduct();
}
然后是两个具体工厂实现
package test;
public class FactoryA implements Factory {
@Override
public Product createProduct() {
return new ProductA();
}
}
package test;
public class FactoryB implements Factory {
@Override
public Product createProduct() {
return new ProductB();
}
}
然后是产品接口
package test;
public interface Product {
public void method();
}
两个产品具体实现
package test;
public class ProductA implements Product {
@Override
public void method() {
System.out.println("A产品");
}
}
package test;
public class ProductB implements Product {
@Override
public void method() {
System.out.println("B产品");
}
}
客户端调用
package test;
public class testA {
public static void main(String[] args) {
Factory factory = new FactoryA();
Product product = factory.createProduct();
product.method();
System.out.println("---------------------------------");
factory = new FactoryB();
product = factory.createProduct();
product.method();
}
}
控制台打印
可以发现,我们需要不同的产品的时候,只需要切换不同的工厂即可,不需要修改任何代码,对扩展开放。
另外也弥补了简单方法模式的不足之处,如果需要增加产品,只需要增加对应产品的实现和对应工厂,而不需要修改任何代码。
学习了工厂方法模式,回想起JDBC代码的结构,就更能理解到为什么要这么写了。JDBC为了统一各个数据库的操作,提供了各个API,统一操作数据库的接口。这样我们就依赖各个抽象,而不是底层的具体的数据库操作(具体生产的过程),印证了前言提到的一句经典的话:用抽象构建框架,用细节扩展实现。我们所用的JDBC的各个API就是抽象的(接口或类),而数据库的具体驱动(生产连接的过程)就是细节,我们所依赖的是API而不是具体的细节。
下面截取部分Driver接口的代码
package java.sql;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
/**
* The interface that every driver class must implement.
*/
public interface Driver {
Connection connect(String url, java.util.Properties info)
throws SQLException;
}
注释上写着,这是一个所有驱动类都必须实现的接口,多么牛逼的一个接口。这个接口就类似与上面的工厂接口,里面的方法connect 就像工厂接口里生产产品的方法,不过这里是生产数据库连接。
下面是Connection接口部分代码
package java.sql;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* <P>A connection (session) with a specific
* database. SQL statements are executed and results are returned
* within the context of a connection.
* <P>
*/
public interface Connection extends Wrapper {
Statement createStatement() throws SQLException;
PreparedStatement prepareStatement(String sql) throws SQLException;
}
这个connection接口就类似于上面的产品接口,这两个方法相信大家都很熟悉吧。每个不同的数据库连接有不同的方法实现。
这是我画的一个草图...原谅我真的很草...由上图可以看出来原来我们熟悉的JDBC正是运用了工厂方法模式,可以知道,工厂方法模式就是提供一个抽象工厂(Driver)和一个抽象产品(Connection),具体工厂类实现就像MySQL的驱动和一个具体的产品MySQL的连接,调用的时候不依赖具体实现,只依赖两个抽象。
当然,在实际使用的时候我们是调用了DriverManager这个类。下面是部分代码
public class Driver extends NonRegisteringDriver
implements java.sql.Driver
{
public Driver()
throws SQLException
{
}
static
{
try
{
DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
}
重点看这个static静态块代码,我们知道我们初始化驱动的时候需要Class.ForName("具体某个数据库的包"),class.forName的作用就是初始化一个类,我们知道,初始化一个类时类的静态代码块也会初始化,且只初始化一次。上面的代码的静态块中调用了DriverManager的方法registerDriver注册一个驱动,这样DriverManager就有了具体某个数据库的驱动,需要获取连接时调用DriverManager的方法getConnection即可,真是太方便了,JDBC什么都给我们封装好了,客户端只需要关心DriverManager的调用(也就相当于只关心抽象工厂与抽象产品)即可。
当然,工厂方法模式在日常还是比较少见的(个人感觉),但是了解这个模式可以让我们更容易理解一些开源项目(就像上面说的JDBC),如果不知道这个模式的话,可能源码看起来就会有一点点云里雾里的感觉,至少我了解了这个设计模式之后,JDBC的大体架构都有了一定的认识。
但是缺点还是有的,如果我要增加一个产品,与之对应又得增加一个具体工厂实现,如果产品成倍增加的话,具体工厂实现也要成倍增加,这样代码就会变得十分庞大。。。后面要说的抽象工厂模式大致可以解决这么一个问题。
注:由于我的水平有限,有些地方说的可能有问题?欢迎大家指出,互相讨论互相学习进步!