# 第04章 工厂方法模式
# 4.1 定义、类型、适用场景及优缺点
定义:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化,推迟到子类中进行;
类型:创建型;
适用场景:
- 创建对象需要大量重复的代码;
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节;
- 一个类通过其子类来指定创建哪个对象;
优点:
- 用户只需要关心所需产品对应的工厂,无须关系创建细节;
- 加入新产品符合开闭原则,提高可扩展性;
缺点:
- 类的个数容易过多,增加复杂度;
- 增加了系统的抽象性和理解难度;
# 4.2 工厂方法示例
代码:
public abstract class Video {
public abstract void produce();
}
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}
public class FEVideo extends Video{
@Override
public void produce() {
System.out.println("录制FE课程视频");
}
}
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public abstract class VideoFactory {
public abstract Video getVideo();
}
public class JavaVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new JavaVideo();
}
}
public class PythonVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new PythonVideo();
}
}
public class FEVideoFactory extends VideoFactory{
@Override
public Video getVideo() {
return new FEVideo();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Test:
public class Test {
public static void main(String[] args) {
VideoFactory videoFactory = new PythonVideoFactory();
VideoFactory videoFactory2 = new JavaVideoFactory();
VideoFactory videoFactory3 = new FEVideoFactory();
Video video = videoFactory.getVideo();
Video video2 = videoFactory2.getVideo();
Video video3 = videoFactory3.getVideo();
video.produce();
video2.produce();
video3.produce();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 4.3 源码中的工厂方法
# 4.3.1 Mybatis源码中的工厂方法
Mybatis的初始化数据使用了工厂方法,mybatis 对于数据源的所有类都在如下包中:
注意:DataSource 接口不是mybatis包下的,是JDK的 javax.sql 包下的。
mybatis 支持三种数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):
① UNPOOLED:**(不使用连接池)**这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单应用程序来说,是一个很好的选择。 不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
② POOLED:**(使用连接池)**这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。
③ JNDI : 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
2
3
4
public class JndiDataSourceFactory implements DataSourceFactory {
public static final String INITIAL_CONTEXT = "initial_context";
public static final String DATA_SOURCE = "data_source";
public static final String ENV_PREFIX = "env.";
private DataSource dataSource;
@Override
public void setProperties(Properties properties) {
try {
InitialContext initCtx;
Properties env = getEnvProperties(properties);
if (env == null) {
initCtx = new InitialContext();
} else {
initCtx = new InitialContext(env);
}
if (properties.containsKey(INITIAL_CONTEXT)
&& properties.containsKey(DATA_SOURCE)) {
Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
} else if (properties.containsKey(DATA_SOURCE)) {
dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
}
} catch (NamingException e) {
throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
}
}
@Override
public DataSource getDataSource() {
return dataSource;
}
private static Properties getEnvProperties(Properties allProps) {
final String PREFIX = ENV_PREFIX;
Properties contextProperties = null;
for (Entry<Object, Object> entry : allProps.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (key.startsWith(PREFIX)) {
if (contextProperties == null) {
contextProperties = new Properties();
}
contextProperties.put(key.substring(PREFIX.length()), value);
}
}
return contextProperties;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class UnpooledDataSourceFactory implements DataSourceFactory {
private static final String DRIVER_PROPERTY_PREFIX = "driver.";
private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();
protected DataSource dataSource;
public UnpooledDataSourceFactory() {
this.dataSource = new UnpooledDataSource();
}
@Override
public void setProperties(Properties properties) {
Properties driverProperties = new Properties();
MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
for (Object key : properties.keySet()) {
String propertyName = (String) key;
if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
String value = properties.getProperty(propertyName);
driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
} else if (metaDataSource.hasSetter(propertyName)) {
String value = (String) properties.get(propertyName);
Object convertedValue = convertValue(metaDataSource, propertyName, value);
metaDataSource.setValue(propertyName, convertedValue);
} else {
throw new DataSourceException("Unknown DataSource property: " + propertyName);
}
}
if (driverProperties.size() > 0) {
metaDataSource.setValue("driverProperties", driverProperties);
}
}
@Override
public DataSource getDataSource() {
return dataSource;
}
private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
Object convertedValue = value;
Class<?> targetType = metaDataSource.getSetterType(propertyName);
if (targetType == Integer.class || targetType == int.class) {
convertedValue = Integer.valueOf(value);
} else if (targetType == Long.class || targetType == long.class) {
convertedValue = Long.valueOf(value);
} else if (targetType == Boolean.class || targetType == boolean.class) {
convertedValue = Boolean.valueOf(value);
}
return convertedValue;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
}
2
3
4
5
# 4.3.2 JDK源码中的工厂方法
# Collection中的iterator方法(实际上是抽象工厂)
java.util.Collection
接口中定义了一个抽象的iterator()
方法,该方法就是一个工厂方法。
对于iterator()
方法来说Collection
就是一个根抽象工厂,下面还有List
等接口作为抽象工厂,再往下有ArrayList
等具体工厂。
java.util.Iterator
接口是根抽象产品,下面有ListIterator
等抽象产品,还有ArrayListIterator
等作为具体产品。
使用不同的具体工厂类中的iterator
方法能得到不同的具体产品的实例。
# JDBC数据库开发
在使用JDBC
进行数据库开发时,如果数据库由MySQL改为Oracle或其他,则只需要改一下数据库驱动名称就可以,其他都不用修改(前提是使用的都是标准SQL语句)。或者在Hibernate框架中,更换数据库方言也是类似道理
← 第03章 简单工厂 第05章 抽象工厂模式 →