本文节选自《设计模式就该这样学》

1 对于产品等级构造和产品族

在解说形象工厂之前,咱们要理解两个概念:产品等级构造和产品族,如下图所示。

上图中有正方形、圆形和菱形3种图形,雷同色彩、雷同深浅的代表同一个产品族,雷同形态的代表同一个产品等级构造。同样能够从生存中来举例,比方,美的电器生产多种家用电器,那么上图中,色彩最深的正方形就代表美的洗衣机,色彩最深的圆形代表美的空调,色彩最深的菱形代表美的热水器,色彩最深的一排都属于美的品牌,都属于美的电器这个产品族。再看最右侧的菱形,色彩最深的被指定了代表美的热水器,那么第二排色彩略微浅一点的菱形代表海信热水器。同理,同一产品族下还有格力洗衣机、格力空调、格力热水器。

再看下图,最左侧的小房子被认为是具体的工厂,有美的工厂、海信工厂、格力工厂。每个品牌的工厂都生产洗衣机、空调和热水器。

通过下面两张图的比照了解,置信大家对形象工厂有了十分形象的了解。

2 形象工厂模式的通用写法

以下是形象工厂模式的通用写法。

public class Client {    public static void main(String[] args) {        IFactory factory = new ConcreteFactoryA();        factory.makeProductA();        factory.makeProductB();        factory = new ConcreteFactoryB();        factory.makeProductA();        factory.makeProductB();    }    //形象工厂类    public interface IFactory {        IProductA makeProductA();        IProductB makeProductB();    }    //产品A形象    public interface IProductA {        void doA();    }    //产品B形象    public interface IProductB {        void doB();    }    //产品族A的具体产品A    static class ConcreteProductAWithFamilyA implements IProductA{        public void doA() {            System.out.println("The ProductA be part of FamilyA");        }    }    //产品族A的具体产品B    static class ConcreteProductBWithFamilyA implements IProductB{        public void doB() {            System.out.println("The ProductB be part of FamilyA");        }    }    //产品族B的具体产品A    static class ConcreteProductAWithFamilyB implements IProductA{        public void doA() {            System.out.println("The ProductA be part of FamilyB");        }    }    //产品族B的具体产品B    static class ConcreteProductBWithFamilyB implements IProductB{        public void doB() {            System.out.println("The ProductB be part of FamilyB");        }    }    //具体工厂类A    static class ConcreteFactoryA implements IFactory{        public IProductA makeProductA() {            return new ConcreteProductAWithFamilyA();        }        public IProductB makeProductB() {            return new ConcreteProductBWithFamilyA();        }    }    //具体工厂类B    static class ConcreteFactoryB implements IFactory{        public IProductA makeProductA() {            return new ConcreteProductAWithFamilyB();        }        public IProductB makeProductB() {            return new ConcreteProductBWithFamilyB();        }    }}

3 应用形象工厂模式反对产品扩大

咱们来看一个具体的业务场景,并且用代码来实现。还是以网络课程为例,个别课程研发会有肯定的规范,每个课程不仅要提供课程的录播视频,还要提供老师的课堂笔记。相当于当初的业务变更为同一个课程不单纯是一个课程信息,要同时蕴含录播视频、课堂笔记,甚至要提供源码能力形成一个残缺的课程。首先在产品等级中减少两个产品:录播视频IVideo和课堂笔记INote。
IVideo接口的代码如下。

public interface IVideo {    void record();}

INote接口的代码如下。

public interface INote {    void edit();}

而后创立一个形象工厂CourseFactory类。

/** * 形象工厂是用户的主入口 * 在Spring中利用得最为宽泛的一种设计模式 * 易于扩大 * Created by Tom */public abstract class CourseFactory {    public void init(){        System.out.println("初始化根底数据");    }    protected abstract INote createNote();    protected abstract IVideo createVideo();}

接下来创立Java产品族,Java视频JavaVideo类的代码如下。

public class JavaVideo implements IVideo {    public void record() {        System.out.println("录制Java视频");    }}

扩大产品等级Java课堂笔记JavaNote类。

public class JavaNote implements INote {    public void edit() {        System.out.println("编写Java笔记");    }}

创立Java产品族的具体工厂JavaCourseFactory。

public class JavaCourseFactory extends CourseFactory {    public INote createNote() {        super.init();        return new JavaNote();    }    public IVideo createVideo() {        super.init();        return new JavaVideo();    }}

随后创立Python产品族,Python视频PythonVideo类的代码如下。

public class PythonVideo implements IVideo {    public void record() {        System.out.println("录制Python视频");    }}

扩大产品等级Python课堂笔记PythonNote类。

public class PythonNote implements INote {    public void edit() {        System.out.println("编写Python笔记");    }}

创立Python产品族的具体工厂PythonCourseFactory。

public class PythonCourseFactory implements CourseFactory {    public INote createNote() {        return new PythonNote();    }    public IVideo createVideo() {        return new PythonVideo();    }}

最初来看客户端调用代码。

public static void main(String[] args) {    JavaCourseFactory factory = new JavaCourseFactory();    factory.createNote().edit();    factory.createVideo().record();}

下面代码残缺地形容了Java课程和Python课程两个产品族,也形容了视频和笔记两个产品等级。形象工厂十分完满、清晰地形容了这样一层简单的关系。然而,不晓得大家有没有发现,如果再持续扩大产品等级,将源码Source也退出课程中,则代码从形象工厂到具体工厂要全副调整,这显然不合乎开闭准则。

4 应用形象工厂模式重构数据库连接池

还是演示课堂开始的JDBC操作案例,咱们每次操作都须要从新创立数据库连贯。其实每次创立都十分消耗性能,耗费业务调用工夫。咱们应用形象工厂模式,将数据库连贯事后创立好,放到容器中缓存着,当业务调用时就只需现取现用。咱们来看代码。
Pool抽象类的代码如下。

/** * 自定义连接池getInstance()返回POOL惟一实例,第一次调用时将执行构造函数 * 构造函数Pool()调用驱动装载loadDrivers()函数; * 连接池创立createPool()函数,loadDrivers()装载驱动 * createPool()创立连接池,getConnection()返回一个连贯实例, * getConnection(long time)增加工夫限度 * freeConnection(Connection con)将con连贯实例返回连接池,getnum()返回闲暇连接数 * getnumActive()返回以后应用的连接数 * * @author Tom * */public abstract class Pool {   public String propertiesName = "connection-INF.properties";   private static Pool instance = null;     //定义惟一实例   /**    * 最大连接数    */   protected int maxConnect = 100;         //最大连接数   /**    * 放弃连接数    */   protected int normalConnect = 10;     //放弃连接数   /**    * 驱动字符串    */   protected String driverName = null;     //驱动字符串   /**    * 驱动类    */   protected Driver driver = null;         //驱动变量   /**    * 公有构造函数,不容许外界拜访    */   protected Pool() {      try      {         init();         loadDrivers(driverName);      }catch(Exception e)      {         e.printStackTrace();      }   }   /**    * 初始化所有从配置文件中读取的成员变量    */   private void init() throws IOException {      InputStream is = Pool.class.getResourceAsStream(propertiesName);      Properties p = new Properties();      p.load(is);      this.driverName = p.getProperty("driverName");      this.maxConnect = Integer.parseInt(p.getProperty("maxConnect"));      this.normalConnect = Integer.parseInt(p.getProperty("normalConnect"));   }   /**    * 装载和注册所有JDBC驱动程序    * @param dri  接管驱动字符串    */   protected void loadDrivers(String dri) {      String driverClassName = dri;      try {         driver = (Driver) Class.forName(driverClassName).newInstance();         DriverManager.registerDriver(driver);         System.out.println("胜利注册JDBC驱动程序" + driverClassName);      } catch (Exception e) {         System.out.println("无奈注册JDBC驱动程序:" + driverClassName + ",谬误:" + e);      }   }   /**    * 创立连接池    */   public abstract void createPool();   /**    *    *(单例模式)返回数据库连接池Pool的实例    *    * @param driverName 数据库驱动字符串    * @return    * @throws IOException    * @throws ClassNotFoundException    * @throws IllegalAccessException    * @throws InstantiationException    */   public static synchronized Pool getInstance() throws IOException,         InstantiationException, IllegalAccessException,         ClassNotFoundException {      if (instance == null) {         instance = (Pool) Class.forName("org.e_book.sqlhelp.Pool").newInstance();      }      return instance;   }   /**    * 取得一个可用的连贯,如果没有,则创立一个连贯,并且小于最大连贯限度    * @return    */   public abstract Connection getConnection();   /**    * 取得一个连贯,有工夫限度    * @param time 设置该连贯的持续时间(以毫秒为单位)    * @return    */   public abstract Connection getConnection(long time);   /**    * 将连贯对象返回连接池    * @param con 取得连贯对象    */   public abstract void freeConnection(Connection con);   /**    * 返回以后闲暇的连接数    * @return    */   public abstract int getnum();   /**    * 返回当前工作的连接数    * @return    */   public abstract int getnumActive();   /**    * 敞开所有连贯,撤销驱动注册(此办法为单例办法)    */   protected synchronized void release() {      //撤销驱动      try {         DriverManager.deregisterDriver(driver);         System.out.println("撤销JDBC驱动程序 " + driver.getClass().getName());      } catch (SQLException e) {         System.out               .println("无奈撤销JDBC驱动程序的注册:" + driver.getClass().getName());      }   }}

DBConnectionPool数据库连接池的代码如下。

/** * 数据库连接池治理类 * @author Tom * */public final class DBConnectionPool extends Pool {   private int checkedOut;                         //正在应用的连接数   /**        * 寄存产生的连贯对象容器    */   private Vector<Connection> freeConnections = new Vector<Connection>();                                                 //寄存产生的连贯对象容器   private String passWord = null;                 //明码   private String url = null;                     //连贯字符串   private String userName = null;                 //用户名   private static int num = 0;                    //闲暇连接数   private static int numActive = 0;                //以后可用的连接数   private static DBConnectionPool pool = null;    //连接池实例变量   /**    * 产生数据连接池    * @return    */   public static synchronized DBConnectionPool getInstance()   {      if(pool == null)      {         pool = new DBConnectionPool();      }      return pool;   }   /**    * 取得一个数据库连接池的实例    */   private DBConnectionPool() {      try      {         init();         for (int i = 0; i < normalConnect; i++) {     //初始normalConn个连贯            Connection c = newConnection();            if (c != null) {               freeConnections.addElement(c);             //往容器中增加一个连贯对象               num++; //记录总连接数            }         }      }catch(Exception e)      {         e.printStackTrace();      }   }   /**    * 初始化    * @throws IOException    */   private void init() throws IOException   {      InputStream is = DBConnectionPool.class.getResourceAsStream(propertiesName);      Properties p = new Properties();      p.load(is);      this.userName = p.getProperty("userName");      this.passWord = p.getProperty("passWord");      this.driverName = p.getProperty("driverName");      this.url = p.getProperty("url");      this.driverName = p.getProperty("driverName");      this.maxConnect = Integer.parseInt(p.getProperty("maxConnect"));      this.normalConnect = Integer.parseInt(p.getProperty("normalConnect"));   }   /**    * 如果不再应用某个连贯对象,则可调此办法将该对象开释到连接池    * @param con    */   public synchronized void freeConnection(Connection con) {      freeConnections.addElement(con);      num++;      checkedOut--;      numActive--;      notifyAll(); //解锁   }   /**    * 创立一个新连贯    * @return    */   private Connection newConnection() {      Connection con = null;      try {         if (userName == null) { //用户、明码都为空            con = DriverManager.getConnection(url);         } else {            con = DriverManager.getConnection(url, userName, passWord);         }         System.out.println("连接池创立一个新的连贯");      } catch (SQLException e) {         System.out.println("无奈创立这个URL的连贯" + url);         return null;      }      return con;   }   /**    * 返回以后闲暇的连接数    * @return    */   public int getnum() {      return num;   }   /**    * 返回以后可用的连接数    * @return    */   public int getnumActive() {      return numActive;   }   /**    * (单例模式)获取一个可用连贯    * @return    */   public synchronized Connection getConnection() {      Connection con = null;      if (freeConnections.size() > 0) { //还有闲暇的连贯         num--;         con = (Connection) freeConnections.firstElement();         freeConnections.removeElementAt(0);         try {            if (con.isClosed()) {               System.out.println("从连接池删除一个有效连贯");               con = getConnection();            }         } catch (SQLException e) {            System.out.println("从连接池删除一个有效连贯");            con = getConnection();         }        //没有闲暇连贯且以后连贯小于最大允许值,若最大值为0,则不限度      } else if (maxConnect == 0 || checkedOut < maxConnect) {          con = newConnection();      }      if (con != null) { //以后连接数加1         checkedOut++;      }      numActive++;      return con;   }   /**    * 获取一个连贯,并加上等待时间限度,工夫为毫秒    * @param timeout  承受等待时间(以毫秒为单位)    * @return    */   public synchronized Connection getConnection(long timeout) {      long startTime = new Date().getTime();      Connection con;      while ((con = getConnection()) == null) {         try {            wait(timeout); //线程期待         } catch (InterruptedException e) {         }         if ((new Date().getTime() - startTime) >= timeout) {            return null; //如果超时,则返回         }      }      return con;   }   /**    * 敞开所有连贯    */   public synchronized void release() {      try {         //将以后连贯赋值到枚举中         Enumeration allConnections = freeConnections.elements();         //应用循环敞开连接池中的所用连贯         while (allConnections.hasMoreElements()) {            //如果此枚举对象至多还有一个可提供的元素,则返回此枚举的下一个元素            Connection con = (Connection) allConnections.nextElement();            try {               con.close();               num--;            } catch (SQLException e) {               System.out.println("无奈敞开连接池中的连贯");            }         }         freeConnections.removeAllElements();         numActive = 0;      } finally {         super.release();      }   }   /**    * 建设连接池    */   public void createPool() {      pool = new DBConnectionPool();      if (pool != null) {         System.out.println("创立连接池胜利");      } else {         System.out.println("创立连接池失败");      }   }}

5 形象工厂模式在Spring源码中的利用

在Spring中,所有工厂都是BeanFactory的子类。通过对BeanFactory的实现,咱们能够从Spring的容器拜访Bean。依据不同的策略调用getBean()办法,从而取得具体对象。

public interface BeanFactory {    String FACTORY_BEAN_PREFIX = "&";    Object getBean(String name) throws BeansException;    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;    Object getBean(String name, Object... args) throws BeansException;    <T> T getBean(Class<T> requiredType) throws BeansException;    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;    boolean containsBean(String name);    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBean     DefinitionException;    @Nullable    Class<?> getType(String name) throws NoSuchBeanDefinitionException;    String[] getAliases(String name);}

BeanFactory的子类次要有ClassPathXmlApplicationContext、XmlWebApplicationContext、StaticWebApplicationContext、StaticPortletApplicationContext、GenericApplicationContext和Static ApplicationContext。在Spring中,DefaultListableBeanFactory实现了所有工厂的公共逻辑。

【举荐】Tom弹架构:珍藏本文,相当于珍藏一本“设计模式”的书
本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我高兴!
如果本文对您有帮忙,欢送关注和点赞;如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源。关注微信公众号『 Tom弹架构 』可获取更多技术干货!