您的位置:首页 > 编程语言 > Java开发

Java设计模式(2)--简单工厂和工厂方法模式

2016-12-13 09:50 218 查看
一、简单工厂模式

1、概念

从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式(GOF的意思是由四个人所著的一本《设计模式》定义了23种设计模式,通常用GOF代表)之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。

其中,简单工厂式是由一个工厂对象决定创建出哪一种产品的实例,这句话说明了简单工厂的直接原理。下面来详细解读一下简单工厂的原理。

2、原理

下面是一张自己画的简单工厂原理图。



结合原理图,再来理解一下上面的原理文字描述, 有一个产品接口,下面有3种产品,如果我们需要创建这三种产品出来,根据简单工厂的设计思路,首先需要有一个工厂类,里面有创建产品几个方法,然后,调用工厂类,根据所需产品的不同去调用不同的方法,达到创建产品的目的。

这个可以用代码解释,但是由于代码过于简单,这里就不贴了。下面来看看简单工厂的实际可能的应用。

3、使用

这里用一个web项目来解释简单工厂模式。一般来说,我们写servlet是一个请求对应一个servlet,那么这样xml文件配置量将会相当巨大,能不能除去这种配置呢,也就是用一个servletFactory充当一个工厂类,我们需要处理什么请求,就调用相应的Servlet。

要实现这样一个功能,我们需要写一个过滤器,将我们不同的请求分发到对应的地方去。

在这之前,先新建一个servlet工厂类ServleyFactory

/**
* servlet工厂类
* @author Errol
*
*/
public class ServletFactory {

public static Servlet createServlet(String servletName){

if(servletName.equals("login")){
return new LoginServlet();
}else if(servletName.equals("register")){
return new RegisterServlet();
}else if(servletName.equals("reset")){
return new ResetServlet();
}
return null;
}
}


可以看到,我们按照不同的请求,调用了不同的Servlet,分别新建这三个servlet,这里贴一个servlet的代码,其他两个是差不多的:

public class LoginServlet implements Servlet {

@Override
public void destroy() {}

@Override
public ServletConfig getServletConfig() {
return null;
}

@Override
public String getServletInfo() {
return null;
}

@Override
public void init(ServletConfig config) throws ServletException {}

@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String result="登录";
request.setAttribute("result", result);
request.getRequestDispatcher("/index.jsp").forward(request, response);//将数据转发回去
}
}


接下里就是写我们的过滤器,需要实现Filter接口

public class ActionFilter implements Filter {

/*用于截获请求地址,定义好常量*/
private static final String URL_SEPARATOR  = "/";
private static final String SERVLET_PREFIX = "servlet/";

@Override
public void destroy() {}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
String servletName;
//这里是我们处理的主要方法、
//解析url
servletName = parseURL((HttpServletRequest)servletRequest);
//解析之后调用servlet工厂
//这里就是简单工厂的应用
//需要什么servlet,我们就创建什么servlet进行处理
if(servletName != null){
Servlet servlet = ServletFactory.createServlet(servletName);
servlet.service(servletRequest, servletResponse);
}else{
filterChain.doFilter(servletRequest, servletResponse);
}
}

/**
* 解析url,获取到我们应该调用哪一个servlet
* @param servletRequest
*/
private String parseURL(HttpServletRequest httpServletRequest) {
String validUrl = httpServletRequest.getRequestURI().
replaceFirst(httpServletRequest.getContextPath() + URL_SEPARATOR, "");
if(validUrl.startsWith(SERVLET_PREFIX)){
return validUrl.split(SERVLET_PREFIX)[1];
}
return null;

}
@Override
public void init(FilterConfig arg0) throws ServletException {}

}


接下来是配置文件,需要配置这个过滤

<!-- 配置过滤器 -->
<filter>
<filter-name>actionFilter</filter-name>
<filter-class>com.errol.filter.ActionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>actionFilter</filter-name>
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>


注意到,我们的配置文件,只有这个配置,我们把复杂的servlet配置全部简化掉了。

接下来是jsp页面

<body>
<h1>测试简单工厂模式</h1>
<%=request.getAttribute("result") %>
<hr>
<form action="servlet/login" method="get">
<input type="submit" value="登录"/>
</form>
<hr>
<hr>
<form action="servlet/register" method="get">
<input type="submit" value="注册"/>
</form>
<hr>
<hr>
<form action="servlet/reset" method="get">
<input type="submit" value="重置"/>
</form>

</body>


界面



点击登录,结果



测试成功,这就是用到了简单工厂创建不同的Servlet,其中servlet即使原理图中的各个商品。

二、工厂方法模式

1、概念

工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

和简单工厂不同的地方在于,简单工厂是有一个工厂类,通过if else去判断调用哪一个创建产品的函数。而工厂方法是有有一个工厂接口,然后,需要创建什么产品,只需要创建一个工厂子类,实现工厂接口,接着写一个创建函数,只创建该类型的产品。这就将创建产品的公共下放到了子类,而工厂被抽象成为一个接口。

2、原理

来看看工厂方法的原理图



一个产品接口下面三个产品子类,另一边是一个工厂接口,三个工厂子类,各自的职责是分别创建对应的产品,也就是说,如果有新的产品需要创建,只需要实现工厂接口,创建方法内部创建对应的产品即可。

工厂方法的原理也不复杂,代码的简单实现就不写了,主要来看一下实际的应用。

3、使用

下面,我们来拿数据库连接说事。JDBC最重要的两个接口,Driver和Connection,一个是数据库驱动,一个是数据库连接,在Driver接口里,有个方法是connect()返回的就是Connection,获取数据库连接。

我们目前数据库种类繁多,那么java是如何保证兼容这些数据库的呢。答案就在这两个接口上,对于Driver接口,java要求所有的数据库厂商如果想在java领域跑,就必须实现该接口,所以,就出现了比如:com.mysql.jdbc.Driver和oracle.jdbc.driver.OracleDriver,这个是Mysql和oracle对该接口的实现。

其次,java要求所有的数据库需要有Connection的实现,对于mysql和oracle,它们的实现分别是:ConnectionImpl,OracleConnectionWrapper。但是一般这两个实现我们看不到,因为我们可以直接用Connection作为连接的返回,我们并不需要知道具体的数据库连接是什么(这也是抽象的好处)。

好了,这里来理一下它们之间的关系:



这不就是上面所讲的工厂方法的原理图么,驱动对应就是工厂接口,Connection对应就是商品接口,Driver可以创建各式各样的子类来创建各式各样的Conection,简单明了。

注意到最上面有个DriverManager类,这是提供给我们用于管理各式各样的Driver的工具,也即是说,我们不需要直接去接触Driver或者Driver的子类,只需要利用DriverManager就能直接获取到驱动,只需要我们提供驱动类名,数据库的用户名密码(必不可少)。那么这个DriverManager类是如何做到这一点的呢?

通常情况下,我们获取数据库连接代码是这么写的:

Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection();


第一句是什么意思呢?先来解释一下Class.forName(XXX)。Class.forName(XXX)返回的是一个类 ,意思是告诉JVM,现在需要JVM装载一下这个类。Class.forName(XXX)和new不一样,new是创建好了一个对象,创建对象了,其中就包括了装载类,分配内存,调用初始化方法,将对象指向分配的内存地址,而Class.forName(XXX)仅仅是JVM装载了类,但是会执行类中的静态代码块,且仅执行一次。说简单点,就是执行一下com.mysql.jdbc.Driver类中的静态代码块,到这里,我们所有的疑惑的突破点就是com.mysql.jdbc.Driver类中的静态代码块,看源码:

public class Driver extends NonRegisteringDriver
implements java.sql.Driver
{
public Driver()
throws SQLException
{
}

static
{
try
{
DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
}


在静态代码块中,DriverManager出现了,而且还调用了一个注册驱动的方法,入参是驱动对象。恍然大悟,我们DriverManger能够帮我们管理Driver原理就在这里。所有的Driver接口的实现中都有这么一段静态代码快,我们只需要执行这一段代码,就能够完成数据库的驱动,DriverManager中就会持有驱动所必要的信息,而我们并不需要具体知道各家数据库Driver的具体实现。多么方便啊。

还有一个例子,就是ArrayList和HashSet以及他们各自遍历器之间的关系,其实也是工厂方法模式。ArrayList和HashSet上面是iterable接口,而他们遍历器均实现自iterator接口。这就是标准的工厂方法模式。

工厂方法模式其实也有弊端,就当商品种类非常之多时,我们就会有特别多的子类,导致系统非常臃肿。不过对应一些情况下(比如数据库连接)种类不多,这种模式可以极大的方便我们理解和开发。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息