设计模式之Factory

设计模式之 Factory
工厂模式定义:提供建立对象的接口.
为什么使用?
工厂模式是咱们最经常使用的模式了,著名的 Jive 论坛 ,就大量使用了工厂模式,工厂模式在
Java 程序系统能够说是随处可见。
为何工厂模式是如此经常使用?由于工厂模式就至关于建立实例对象的 new,咱们常常要根据
类 Class 生成实例对象,如 A a=new A() 工厂模式也是用来建立实例对象的,因此之后
new 时就要多个心眼,是否能够考虑实用工厂模式,虽然这样作,可能多作一些工做,但会
给你系统带来更大的可扩展性和尽可能少的修改量。
咱们以类 Sample 为例, 若是咱们要建立 Sample 的实例对象:
Sample sample=new Sample();
但是,实际状况是,一般咱们都要在建立 sample 实例时作点初始化的工做,好比赋值 查
询数据库等。
首先,咱们想到的是,可使用 Sample 的构造函数,这样生成实例就写成:
Sample sample=new Sample(参数);
可是,若是建立 sample 实例时所作的初始化工做不是象赋值这样简单的事,多是很长
一段代码,若是也写入构造函数中,那你的代码很难看了(就须要 Refactor 重整)。
为何说代码很难看,初学者可能没有这种感受,咱们分析以下,初始化工做若是是很长一
段代码,说明要作的工做不少,将不少工做装入一个方法中,至关于将不少鸡蛋放在一个篮
子里,是很危险的,这也是有背于Java 面向对象的原则,面向对象的封装(Encapsulation)
和分派(Delegation)告诉咱们,尽可能将长的代码分派“切割”成每段,将每段再“封装”起
来(减小段和段之间偶合联系性),这样,就会将风险分散,之后若是须要修改,只要更改
每段,不会再发生牵一动百的事情。
在本例中,首先,咱们须要将建立实例的工做与使用实例的工做分开, 也就是说,让建立
实例所须要的大量初始化工做从 Sample 的构造函数中分离出去。
这时咱们就须要 Factory 工厂模式来生成对象了,不能再用上面简单 new Sample(参数)。
还有,若是 Sample 有个继承如 MySample, 按照面向接口编程,咱们须要将 Sample 抽象
成一个接口.如今 Sample 是接口,有两个子类 MySample 和 HisSample .咱们要实例化
他们时,以下:
Sample mysample=new MySample();
Sample hissample=new HisSample();
随着项目的深刻,Sample 可能还会"生出不少儿子出来", 那么咱们要对这些儿子一个个
实例化,更糟糕的是,可能还要对之前的代码进行修改:加入后来生出儿子的实例.这在传统
程序中是没法避免的.
但若是你一开始就有意识使用了工厂模式,这些麻烦就没有了.
工厂方法
你会创建一个专门生产 Sample 实例的工厂:
public class Factory{
public static Sample creator(int which){
//getClass 产生 Sample 通常可以使用动态类装载装入类。
if (which==1)
return new SampleA();
else if (which==2)
return new SampleB();
}
}
那么在你的程序中,若是要实例化 Sample 时.就使用
Sample sampleA=Factory.creator(1);
这样,在整个就不涉及到 Sample 的具体子类,达到封装效果,也就减小错误修改的机会,这
个原理能够用很通俗的话来比喻:就是具体事情作得越多,越容易范错误.这每一个作过具体
工做的人都深有体会,相反,官作得越高,说出的话越抽象越笼统,范错误可能性就越少.好
象咱们从编程序中也能悟出人生道理?呵呵.
使用工厂方法 要注意几个角色,首先你要定义产品接口,如上面的 Sample,产品接口下有
Sample 接口的实现类,如 SampleA,其次要有一个 factory 类,用来生成产品 Sample,
以下图,最右边是生产的对象 Sample:
进一步稍微复杂一点,就是在工厂类上进行拓展,工厂类也有继承它的实现类
concreteFactory 了
抽象工厂
工厂模式中有: 工厂方法(Factory Method) 抽象工厂(Abstract Factory).
这两个模式区别在于须要建立对象的复杂程度上。若是咱们建立对象的方法变得复杂了,如
上面工厂方法中是建立一个对象 Sample,若是咱们还有新的产品接口 Sample2.
这里假设:Sample 有两个 concrete 类 SampleA 和 SamleB,而 Sample2 也有两个
concrete 类 Sample2A 和 SampleB2
那么,咱们就将上例中 Factory 变成抽象类,将共同部分封装在抽象类中,不一样部分使用子
类实现,下面就是将上例中的 Factory 拓展成抽象工厂:public abstract class Factory{public abstract Sample creator();public abstract Sample2 creator(Stringname);}public class SimpleFactory extends Factory{public Sample creator(){.........return new SampleA}public Sample2 creator(String name){.........return new Sample2A}}public class BombFactory extends Factory{public Sample creator(){......return new SampleB}public Sample2 creator(String name){......return new Sample2B}}
从上面看到两个工厂各自生产出一套 Sample 和 Sample2,也许你会疑问,为何我不可
以使用两个工厂方法来分别生产 Sample 和 Sample2?
抽象工厂还有另一个关键要点,是由于 SimpleFactory 内,生产 Sample 和生产
Sample2 的方法之间有必定联系,因此才要将这两个方法捆绑在一个类中,这个工厂类有
其自己特征,也许制造过程是统一的,好比:制造工艺比较简单,因此名称叫
SimpleFactory。
在实际应用中,工厂方法用得比较多一些,并且是和动态类装入器组合在一块儿应用,
举例<?XML:NAMESPACE PREFIX = O /><O:P></O:P>
咱们以 Jive 的 ForumFactory 为例,这个例子在前面的 Singleton 模式中咱们讨论过,
如今再讨论其工厂模式:
public abstract class ForumFactory {
private static Object initLock = new Object();
private static String className =
"com.jivesoftware.forum.database.DbForumFactory";
private static ForumFactory factory = null;
public static ForumFactory getInstance(Authorization
authorization) {
//If no valid authorization passed in, return null.
if (authorization == null) {
return null;
}
//如下使用了 Singleton 单态模式
if (factory == null) {
synchronized(initLock) {
if (factory == null) {
......
try {
//动态转载类
Class c = Class.forName(className);
factory = (ForumFactory)c.newInstance();
}
catch (Exception e) {
return null;
}
}
}
}
//Now, 返回 proxy.用来限制受权对 forum 的访问
return new ForumFactoryProxy(authorization, factory,

factory.getPermissions(authorization));
}
//真正建立 forum 的方法由继承 forumfactory 的子类去完成.
public abstract Forum createForum(String name, String
description)
throws UnauthorizedException, ForumAlreadyExistsException;
....
}
由于如今的 Jive 是经过数据库系统存放论坛帖子等内容数据,若是但愿更改成经过文件系
统实现,这个工厂方法 ForumFactory 就提供了提供动态接口:
private static String className =
"com.jivesoftware.forum.database.DbForumFactory";
你可使用本身开发的建立 forum 的方法代替
com.jivesoftware.forum.database.DbForumFactory 就能够.
在上面的一段代码中一共用了三种模式,除了工厂模式外,还有 Singleton 单态模式,以及
proxy 模式,proxy 模式主要用来受权用户对 forum 的访问,由于访问 forum 有两种人:
一个是注册用户 一个是游客 guest,那么那么相应的权限就不同,并且这个权限是贯穿
整个系统的,所以创建一个 proxy,相似网关的概念,能够很好的达到这个效果.
看看 Java 宠物店中的 CatalogDAOFactory:
public class CatalogDAOFactory {
/**
* 本方法制定一个特别的子类来实现 DAO 模式。
* 具体子类定义是在 J2EE 的部署描述器中。
*/
public static CatalogDAO getDAO() throws CatalogDAOSysException
{
CatalogDAO catDao = null;
try {
InitialContext ic = new InitialContext();
//动态装入 CATALOG_DAO_CLASS
//能够定义本身的 CATALOG_DAO_CLASS,从而在无需变动太多代码
//的前提下,完成系统的巨大变动。
String className =(String)
ic.lookup(JNDINames.CATALOG_DAO_CLASS);
catDao = (CatalogDAO)
Class.forName(className).newInstance();
} catch (NamingException ne) {
throw new CatalogDAOSysException("
CatalogDAOFactory.getDAO: NamingException while
getting DAO type : \n" + ne.getMessage());
} catch (Exception se) {
throw new CatalogDAOSysException("
CatalogDAOFactory.getDAO: Exception while getting
DAO type : \n" + se.getMessage());
}
return catDao;
}
}
CatalogDAOFactory 是典型的工厂方法,catDao 是经过动态类装入器 className 获
得 CatalogDAOFactory 具体实现子类,这个实现子类在 Java 宠物店是用来操做
catalog 数据库,用户能够根据数据库的类型不一样,定制本身的具体实现子类,将本身的
子类名给与 CATALOG_DAO_CLASS 变量就能够。
因而可知,工厂方法确实为系统结构提供了很是灵活强大的动态扩展机制,只要咱们更换一
下具体的工厂方法,系统其余地方无需一点变换,就有可能将系统功能进行改头换面的变化。

数据库

相关文章
相关标签/搜索