• 工厂方法模式(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

    废话不多我们直接看类图 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),具体实现都由不同的厂商提供。那么问题来了,居然DriverConnection就能搞定为什么还要一个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%

    复杂度:中低

    变化点:工厂与产品的种类

    选择关键点:工厂类和产品类是否是同生同灭的关系

    逆鳞:无

    相关设计模式

    抽象工厂模式:工厂方法模式与抽象工厂模式最大的区别在于,在工厂方法模式中,工厂创造的是一个产品,而在抽象工厂模式中,工厂创造的是一个产品族。

  • 相关阅读:
    JAVA正则表达式判断元音
    JAVA正则表达式校验qq号码
    方法
    数组
    Java基础随笔3
    LDA背景资料
    scrapy系列(四)——CrawlSpider解析
    scrapy系列(三)——基础spider源码解析
    scrapy系列(二)——startproject、genspider创建项目与模板使用
    我眼中的机器学习(四) 快速寻找最优解
  • 原文地址:https://www.cnblogs.com/learningchencheng/p/7717347.html
Copyright © 2020-2023  润新知