应用场景
定义:工厂方法模式模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
看图说话:
可以看到,上面右半部分是产品抽象和实现体系,左半部分是工厂抽象和实现体系,其中工厂体系依赖于产品体系,每一个工厂负责创造一种产品,这就省去了简单工厂中的elseif判断,又客户端决定实例化一个特定的工厂去创建相应的产品。
普通实例实现
代码实现:
首先是抽象产品接口1
2
3
4
5
6
7public interface Light {
public void turnOn();
public void turnOff();
}
下面是具体的产品
BuldLight产品1
2
3
4
5
6
7
8
9
10
11public class BuldLight implements Light{
public void turnOn() {
System.out.println("BuldLight On");
}
public void turnOff() {
System.out.println("BuldLight Off");
}
}
TubeLight产品1
2
3
4
5
6
7
8
9
10
11public class TubeLight implements Light{
public void turnOn() {
System.out.println("TubeLight On");
}
public void turnOff() {
System.out.println("TubeLight Off");
}
}
抽象的工厂接口1
2
3
4public interface Creator {
public Light createLight();
}
创建指定产品的具体工厂
BuldCreator工厂1
2
3
4
5
6
7public class BuldCreator implements Creator{
public Light createLight() {
return new BuldLight();
}
}
TubeCreator工厂1
2
3
4
5
6public class TubeCreator implements Creator{
public Light createLight() {
return new TubeLight();
}
}
测试类1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Client {
public static void main(String[] args) {
Creator creator = new BuldCreator();
Light light = creator.createLight();
light.turnOn();
light.turnOff();
creator = new TubeCreator();
light = creator.createLight();
light.turnOn();
light.turnOff();
}
}
运行结果:1
2
3
4BuldLight on
BuldLight off
TubeLight on
TubeLight off
可以看到,我们使用可以随意的在具体的工厂和产品之间切换,并且不需要修改任何代码,就可以让原来的程序正常运行,这也是工厂方法模式对扩展开放的表现,另外工厂方法模式弥补了简单工厂模式不满足开闭原则的诟病,当我们需要增加产品时,只需要增加相应的产品和工厂类,而不需要修改现有的代码。
真实实例实现
这个代码没有太大的实际意义。还是来做一个实例,拿数据库来说事吧!
第一个接口Driver1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package java.sql;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
/**
* 任何驱动都必须实现的接口。
*/
public interface Driver {
Connection connect(String url, java.util.Properties info)
throws SQLException;
boolean acceptsURL(String url) throws SQLException;
DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
throws SQLException;
int getMajorVersion();
int getMinorVersion();
boolean jdbcCompliant();
}
每个数据库厂商都必须实现这个接口来提供JDBC服务,即java数据库连接服务。
第一个方法是创建数据库连接,虽然方法名称是connect,但是我觉得这个方法完全可以改为crateConnection。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package java.sql;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* <P>A connection (session) with a specific
* database. SQL statements are executed and results are returned
* within the context of a connection.
* <P>
*/
public interface Connection extends Wrapper {
Statement createStatement() throws SQLException;
PreparedStatement prepareStatement(String sql) throws SQLException;
}
以上两个方法是JDBC API的一部分,也是最重要的部分。它们相当于告诉了数据库生产厂商两个要求。
- 第一,数据库厂商要提供了一个数据库驱动类,他的作用可以是可以创造数据库连接,而这个数据库连接向上转型为我们JDBC的Connection。
第二,数据库厂商要提供一个数据库连接的实现类,这个实现类可以执行具体数据库的各个操作,比如帮我们执行SQL,执行返回结果,关闭连接等等。
我们都知道MySQL的驱动类位于com.mysql.jdbc.Driver,而mysql的connection实现类也在这个包中,名称是ConnectionImpl,而相应的Oracle也有驱动类,位于oracle.jdbc.driver.OracleDriver,相应的oracle也有connection实现类,位于oracle.jdbc.OracleConnectionWrapper。一般每个数据库都会有一个Connection的扩展接口,这个接口的作用是提供使用者针对当前数据库特殊的操作。
工厂方法模式就是提供一个抽象的工厂,一个抽象的产品,在上述当中相当于Driver(数据库连接工厂)和Connection(抽象产品),实现的一方需要提供一个具体的工厂类(比如mysql驱动)和一个具体的产品(比如mysql数据库连接)。
DriverMananger在这个设计当中扮演者一个管理者的角色,它帮我们管理数据库驱动,让我们不需要直接接触驱动接口,我们获取连接只需要和DriverManager打交道就可以,也就是说客户端依赖于DriverManager和Connection就可以完成工作,不再需要与Driver关联,所以上述说我们依赖于Driver和Connection,现在DriverManager帮我们管理Driver,那我们只需要依赖于DriverManager和Connection就可以了。
mysql源代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public 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!");
}
}
}可以看到,在类构造方法中,加入了registerDriver这个方法,所以当我们使用class.forName加载驱动的时候,将会把mysql驱动注册到DriverManager,这时DriverManager中就会持有Mysql驱动所必要的信息,我们就可以使用DriverManager来获得具体的mysql连接了,当然,你要提供url,用户名和密码。
工厂方法模式的好处和适用的场景都相对比较好理解。
从类关系上来说,它可以让客户端与具体的工厂与产品解耦,从业务角度来说,它让客户端与具体的产品解耦。
适用的场景就是我们需要一个产品帮我们完成一项任务,但是这个产品有可能有很多品牌(像这里的mysql,oracle),为了保持我们对产品操作的一致性,我们就可能要用到工厂方法模式。
假设产品数量巨多,而且需要我们亲手去逐个实现的时候,工厂方法模式就会增加系统的复杂性,到处都是工厂类和产品类,而且这里所说的工厂类和产品类只是概念上的,真正的产品可能不是一两个类就能搞定,否则mysql和oracle的驱动包为啥要那么多类,而不是就一个Driver和一个Connection。总结
两种使用方式一种是对使用者透明的,一种是不透明的,一种是使用者对具体的产品不关心,这种情况下,一般产品提供的功能是类似的。一种是使用者非常了解产品的特性,并想使用产品的特性,这种情况下,一般产品只提供最基本的一致的功能,但每个产品都会有自己独特的一面。
真正做项目的过程当中很少用到工厂方法模式,这个模式更多的是帮助我们理解现有的开源项目,就像现在,你是不是对JDBC的大体框架有了一定认识了呢,如果你不知道这个模式,可能看源码会觉得一头雾水呢。
规则只是用来指导你的,不是用来限制你的,只要设计合理,你的设计就是规则!- 需要声明一个注解,它可以用来给servlet标识它的名称。
- 需要声明一个注解的处理器,用来处理我们的注解,主要作用是通过一个CLASS文件,去获得它的注解信息。
- 基于性能,我们需要将servlet与名称的映射与应用的生命周期绑定,并且这份映射在整个应用当中有且仅有一份,且不可更改。
- 让我们用于分派请求的过滤器,使用映射信息将客户请求对应到相应的servlet去处理,并且将分派逻辑移回过滤器,从而彻底删除简单工厂,即ServletFactory。