本文节选自《设计模式就该这样学》
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 弹架构』可获取更多技术干货!