设计模式-工厂方法模式

引言


为什么要引入工厂方法模式?

前文已经详细的讲解了简单工厂模式,既然已经有了简单工厂模式,为什么还要有工厂方法模式呢?
相信看完前文已经明确的知道简单工厂模式有以下不足:
  • 把实例的创建(在这里实际是指产品类的实例化)都放在了工厂类中,这样做的结果是非常危险的,一旦这个类出现任何问题,那么整个系统就game over了;
  • 不符合设计模式思想的“开-闭”原则。“开-闭”原则的核心就是在不更改现有系统的结构上实现新的功能,工厂类明显不符合,一旦增加新的产品,工厂类的原有代码肯定是需要更改的,结构肯定会有变化的。
引入工厂方法模式就是为了弥补这两个不足。那么工厂方法模式是怎么来弥补这些不足的呢?
核心的工厂类不再负责所有的产品的创建,将具体创建的工作交给子类去做,换句话说,也就是原有的工厂类变成了一个抽象工厂的角色,仅负责给出具体工厂子类必须实现的接口。


工厂方法模式定义

工厂方法模式是类的创建模式,又被称为虚拟构造子模式或者多态性工厂模式。它的用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。

工厂方法模式结构与角色

在说它的结构与角色之前还是先看看类图,这样稍微明显易懂些。

从上图可以看出,工厂方法模式有有以下这些角色:
  • 抽象工厂(Creator)角色:工厂方法模式核心,与应用程序无关,在实际的系统中,这个角色常常使用抽象Java类实现;
  • 具体工厂(Concrete Creator)角色:实现抽象工厂接口,该角色含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象;
  • 抽象产品(Product)角色
  • 具体产品(Concrete Product)角色


自定义工厂方法模式

照样只是给一个简单的demo,给出一个工厂方法模式的模板。
  • 需求:某一个商业软件产品需要支持Mysql和Oracle数据库,给出一个简单的设计,保证系统可以根据客户端的需要,随时向Mysql和Oracle数据库引擎发起查询请求;
  • 抽象设计:
    • 首先确定使用哪种设计模式比较合适,很明显,这个需求使用工厂方法模式来实现会更好;
    • 确定好设计模式之后,接下来该抽象出各个角色了:
      • 抽象工厂角色:抽象出查询类QueryRunner;
      • 具体工厂角色:mysql查询和oracle查询;
      • 抽象产品角色:查询结果集,ResultSet;
      • 具体产品角色:mysql查询返回的ResultSet,oracle查询返回的ResultSet
    • 类图:


  • 具体代码实现:
QueryRunner:
public abstract class QueryRunner {

    public ResultSet run() throws Exception {
        Connection connection = createConnection();
        String sql = createSql();
        return run(connection, sql);
    }

    protected abstract Connection createConnection() throws ClassNotFoundException, SQLException;

    protected abstract String createSql();

    protected abstract ResultSet run(Connection connection, String sql) throws SQLException;

}
MySQLQueryRunner:
public class MysqlQueryRunner extends QueryRunner {

    String driver = "com.mysql.jdbc.Driver";

    String url = "jdbc:mysql://localhost:3306/test";

    String user = "root";

    String password = "asdfasdf";

    protected Connection createConnection() throws ClassNotFoundException, SQLException {
        Class.forName(driver);
        Connection connection = DriverManager.getConnection(url, user, password);
        return connection;
    }

    protected String createSql() {
        return "select * from user";
    }

    protected ResultSet run(Connection connection, String sql) throws SQLException {
        Statement statement = connection.createStatement();
        return statement.executeQuery(sql);
    }
}
OracleQueryRunner:
public class OracleQueryRunner extends QueryRunner {

    private String driver = "oracle.jdbc.driver.OracleDriver";

    String url = "jdbc:oracle:thin:@127.0.0.1:1521:test";

    String user = "root";

    String password = "asdfasdf";

    protected Connection createConnection() throws ClassNotFoundException, SQLException {
        Class.forName(driver);
        Connection connection = DriverManager.getConnection(url, user, password);
        return connection;
    }

    protected String createSql() {
        return "select * from user";
    }

    protected ResultSet run(Connection connection, String sql) throws SQLException {
        Statement statement = connection.createStatement();
        return statement.executeQuery(sql);
    }
}
DataBaseClient:
public class DataBaseClient {

    private static QueryRunner queryRunner;

    public static void main(String[] args) throws Exception {
        queryRunner = new OracleQueryRunner();
        ResultSet resultSet = queryRunner.run();
        //操作resultSet,这里的resultSet就是具体的产品
    }

}

工厂方法模式在Java中的应用


在Collection中的应用

相信看过jdk源码的都java的Collection吧,在说Collection之前还是先说说聚集吧。
何谓聚集?
Java聚集是Java 1.2提出来的,称多个对象聚在一起形成的总体为聚集,聚集对象时能够包容一组的容器对象。
继续说工厂模式在Collection中的应用,Collection接口规定所有实现接口的子类必须要提供一个iterator方法,返回一个Iterator类型的对象,可以看出iterator方法就是一个工厂方法。


URL与URLConnection的应用

  • URL类简要介绍:
URL类代表一个Uniform Resource Locator,换句话说也就是互联网资源的一个指针。互联网的一个资源,可能是一个简单的文件、目录,也可以是一个更加复杂的对象。
  • 如何创建URL对象:

创建一个URL对象很简单,主需要将一个合法的URL传入URL的构造方法中即可,代码如下:

URL url = new URL("http://www.baidu.com");

  • 工厂方法模式分析:

URL对象提供了一个叫做openConnection()的工厂方法:

public URLConnection openConnection() throws java.io.IOException {
        return handler.openConnection(this);
    }

该方法返回URLConnection对象,该对象代表一个与远程对象的连接。同时,URLConnection是所有的代表应用系统与一个URL的连接对象的基类,使用URLConnection可以对任意一个URL进行读写操作。

看到这里可能还是很蒙圈,来个类图吧,相信看了类图就可以一目了然了。



由类图可以看出来:

    • 工厂类:URL;
    • 抽象产品类:URLConnection
    • 具体产品类:HttpURLConnection、JarURLConnection

  • 简单实例分析获取互联网资源步骤:
public class URLTest {
    @Test
    public void run() throws Exception {
        URL url = new URL("http://www.baidu.com");
        URLConnection connection = url.openConnection();
        BufferedReader readerIn = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String inputLine;
        while ((inputLine = readerIn.readLine()) != null) {
            System.out.println(inputLine);
        }
        //不要忘了关闭流
        readerIn.close();
    }
}

可以看出,该例子在运行时的活动顺序如下:

  1. 创建一个以“http://www.baidu.com”为目标的URL对象;
  2. 调用URL对象的openCollection()方法,得到一个“http://www.baidu.com”的远程连接对象;
  3. 客户端调用URLConnection对象的getInputStream()方法读入远程URL的数据(运行时会打印出“http://www.baidu.com”页面的全部HTML源代码)

工厂方法模式在许多框架都有明显的应用,在这里不一一做讲解。有兴趣的可以自行阅读源码。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值