在数据持久层中,数据源是一个非常重要的组件,其性能直接关系到整个数据持久层的性能 。在实践中比较常见的第三方数据源组件有 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 负 责创建 。 如图所示 ,这里也使用了

工厂方法模式。

工厂数据采集架构图_工厂数据采集架构图_02

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 进行管理的,自身没有提供太多相关的功能。