在数据持久层中,数据源是一个非常重要的组件,其性能直接关系到整个数据持久层的性能 。在实践中比较常见的第三方数据源组件有 Apache Common DBCP,C3PO 、 Proxool 等, MyBatis不仅可以集成第三方数据源组件,还提供了自己的数据源实现。这个过程是一个典型的工厂模式。
另外还有事务的设计也是工厂模式。
1. 什么是工厂模式
1.1介绍
工厂模式具体包含简单工厂模式,工厂方法模式和抽象工厂模式三个类型。
简单工厂就是通过传入的参数来决定创建哪种类型的工厂。简单工厂有一个具体的工厂类,可以生成多个不同类型的产品。
工厂模式适用于产品种类相对较少场景,可以方便快速创建不同的产品。
工厂模式要点
简单工厂模式(静态工厂模式),虽然某个程度不符合设计原则,但实际使用最多。 工厂方法模式:不修改已有类的前提下,通过增加新的工厂类实现扩展。 抽象工厂模式:不可以增加产品,可以增加产品族。
简单工厂就是我们通常写的if else型的代码,根据参数决定创建什么对象。
工厂模式是典型的解耦框架,符合迪米特,依赖倒转和里式替换原则。
简单工厂中,主要包含3中角色:
1.抽象产品,接口 ,定义要创建的产品的属性。
2.具体产品:抽象产品的具体实现类。
3.简单工厂,根据输入的参数决定创建哪个产品。
编程心得:
1.2简单工厂
优点:结构简单,根据外部给定的参数快速创建需要的产品。
缺点:工厂类单一,负责所有产品的创建,当产品比较多或者变化比较大时难以维护,违背高聚合原则。
接口:
public interface ICourse {
void record();
}
两个具体创建类:
package ch2_factory.simple;
public class JavaCourse implements ICourse {
public void record() {
System.out.println("录制java课程");
}
}
package ch2_factory.simple;
public class PythonCourse implements ICourse {
public void record() {
System.out.println("录制python课程");
}
}
简单工厂:
package ch2_factory.simple;
public class CourseFactory {
public ICourse createCourse(String parame) {
if ("java".equals(parame)) {
return new JavaCourse();
} else if ("python".equals(parame)) {
return new PythonCourse();
} else {
return null;
}
}
}
调用接口:
package ch2_factory.simple;
public class MainTest {
public static void main(String[] args) {
CourseFactory courseFactory = new CourseFactory();
ICourse course = courseFactory.createCourse("java");
course.record();
}
}
1.3 工厂方法
工厂方法也叫多态性工厂模式,将创工厂进行了下沉和抽象,将工厂定义为一个接口,由该接口的实现类决定创建什么产品。
工厂方法模式适用于简单工厂模式的不足的场景,或者创建对象需要大量重复的代码等情况。
抽象工厂有四个角色:
1.抽象产品:定义要创建的产品的属性。
2.具体产品:实际需要的产品的实现类。
3.抽象工厂:工厂创建这些产品的方法是什么
4.具体工厂:根据具体产品的情况,分别是创建。
工厂方法的优点:
(1)比较灵活,新产品的创建比较容易,新添加产品和对应工厂的实现类即可。
(2)解耦效果好。
缺点:
(1)类的个数比较多,代码量比较多。
(2)增加了系统的抽象性和理解的难度。
工厂模式的创建步骤:
创建一个实体的抽象接口,然后有几个实体就有建立几个实现类:
public interface Car{
public void run();
}
public Audi implements Car{
public void run(){System.out.println("Audi run")};
}
public Byd implements Car{
public void run(){System.out.println("Byd run"};
}
2.定义一个工厂接口,有几个实体类就建立几个工厂实现类。
public interface CarFactory{
public void createCar();
}
public class AudiFactory{
private Car car;
public Car createCar(
car=new Audi();
return car;
);
}
public class BydFactory{
private Car car;
public Car createCar(
car=new Byd();
return car;
);
}
3.格式为 实体抽象接口 实体名=new 工厂实现类.创建方法
Car car=new AudiFactory.createCar();
car.run();
Car car2=new BydFactory.createCar();
car2.run();
1.4 抽象工厂模式
围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂。
对于增加新的产品,无能为力;支持增加产品族
抽象工厂模式是工厂方法模式的升级版,在有多个业务品种,业务分类时,通过抽象工厂模式产生需要的对象时一种非常好的解决方式。 意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
关键代码:在一个工厂里聚合多个同类产品。
抽象工厂适用于需要生产产品族的情景。
抽象工厂的角色也有四个:
1.抽象产品:为一类产品对象声明一个接口。
2.具体产品: 实现创建的具体产品对象的操作。
3.抽象工厂:声明创建抽象产品对象的一个操作接口。
4.具体工厂: 实现创建具体产品的操作。
创建和使用步骤:
确定有几个组件,例如车子有轮胎,发动机,靠椅等,为每个分别建立接口和不同方式的实现类,例如高中低档次等。
建立工厂接口,只记录包含的组件。例如车子,包含创建轮胎,发动机,和靠椅等。然后根据不同的要求建立工厂实现类,实现的接口就是1中的不同实现类。
直接确定使用哪个工厂实现类即可。这一层创建出来的就是各个组件。
发动机组件:
public interface Engine {
void run();
void start();
}
class LuxuryEngine implements Engine {
@Override
public void run() {
System.out.println("转得快");
}
@Override
public void start() {
System.out.println("启动快");
}
}
class LowerEngine implements Engine {
@Override
public void run() {
System.out.println("转得慢");
}
@Override
public void start() {
System.out.println("启动慢");
}
}
轮胎组件:
public interface Lyre {
void revolve();
}
class LuxuryTyre implements Lyre {
@Override
public void revolve() {
System.out.println("不磨损");
}
}
class LowerTyre implements Lyre {
@Override
public void revolve() {
System.out.println("磨损快");
}
}
座椅组件:
public interface Seat {
void massage();
}
class LuxurySeat implements Seat {
@Override
public void massage() {
System.out.println("可以自动按摩");
}
}
class LowSeat implements Seat {
@Override
public void massage() {
System.out.println("不能自动按摩");
}
}
然后物体的抽象接口,然后有几种情况就给出几个实现:
public interface CarFactory {
Lyre createLyre();
Engine createEngine();
Seat createSeat();
}
public class LoweryCarFactory implements CarFactpory {
@Override
public Lyre createLyre() {
return new LowerTyre();
}
@Override
public Engine createEngine() {
return new LowerEngine();
}
@Override
public Seat createSeat() {
return new LowSeat();
}
}
public class LuxuryCarFactory implements CarFactpory {
@Override
public Lyre createLyre() {
return new LuxuryTyre();
}
@Override
public Engine createEngine() {
return new LuxuryEngine();
}
@Override
public Seat createSeat() {
return new LuxurySeat();
}
}
调用:
public static void main(String[] args) {
LoweryCarFactory loweryCarFactory = new LoweryCarFactory();
Engine engine= loweryCarFactory.createEngine();
engine.run();
engine.start();
//或者这样直接调用
loweryCarFactory.createLyre().revolve();
loweryCarFactory.createSeat().massage();
}
2. 工厂模式在数据源的应用
常见的数据源组件都实现了 javax.sql.DataSource 接口, MyBatis自身实现的数据源实现也不例外。 MyBatis 提供了两个 javax.sql.DataSource 接口实现,分别是 PooledDataSource 和UnpooledDataSource 。 Mybatis 使用不同的 DataSourceFactory 接口实现创建不同类型的DataSource ,如图所示,这是工厂方法模式的一个典型应用。
在数据源模块中, DataSourceFactory 接口扮演工厂接口的角色 。 UnpooledDataSourceFactory和 PooledDataSourceFactory 则扮演着具体工厂类的角色。我们从 DataSourceFactory 接口开始分析,其定义如下:
public interface DataSourceFactory {
//设置 Data Source 的相关属性,一般紧跟在初始化完成之后
void setProperties(Properties props);
//获取 DataSource 对象
DataSource getDataSource();
}
在 UnpooledDataSourceFactory 的构造函数中会直接创建 UnpooledDataSource 对象,并初始化 UnpooledDataSourceFactory.dataSource 宇段。 UnpooledDataSourceFactory.setProperties()方法会
完成对 UnpooledDataSource 对象的配置,代码如下:
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);
}
}
Unpoo ledDataSourceFactory.getDataSource()方法实现比较简单 ,它直接返回 dataSource 字段
记录的 UnpooledDataSource 对象 。
PooledDataSourceFactory 继承了 UnpooledDataSourceFactory ,但并没有覆盖 setProperties()
方法和 getDataSource()方法。两者唯一 的区别是 PooledDataSourceFactory 的构造函数会将其
dataSource 字段初始化为 PooledDataSource 对象。
3.工厂模式在事务中的应用
在实践开发 中, 控制数据库事务是一件非常重要的工作, MyBatis 使用 Transaction 接口对数据库事务进行了 抽象,
Transaction 接口有 JdbcTransaction 、 ManagedTransaction 两个实现,其对象分别 由
JdbcTransactionFactory 和 ManagedTransactionFactory 负 责创建 。 如图所示 ,这里也使用了
工厂方法模式。
Transaction 接口的定义如下 :
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
TransactionFactory 接口定义了配置新建 TransactionFactory 对象的 方法 ,以 及 创 建
Transaction 对象的方法,代码如下 :
public interface TransactionFactory {
default void setProperties(Properties props) {
// NOP
}
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
JdbcTransactionFactory 和 ManagedTransactionFactory 负责创建 JdbcTransaction 和
ManagedTransaction。
不过MyBatis 通常会与Spring集成使用, 数据库的事务是交给 Spring 进行管理的,自身没有提供太多相关的功能。