1. 程式人生 > >簡單工廠模式與工廠方法模式以及Mybatis中工廠模式的使用

簡單工廠模式與工廠方法模式以及Mybatis中工廠模式的使用

一、簡單工廠模式

Java是一門面向物件的語言。有很多時候我們需要去生產物件,工廠模式就是我們生產物件的一種方式。


比如這樣一個場景: 我們需要做一個兩個數之間的運算,但我們不知道使用者究竟要使用哪種運算子對其進行運算。我們就可以使用工廠模式,對使用者的選擇做一個判斷,從而只返回一個使用者需要的結果。

第一步,操作實體類,封裝兩個運算數:

注意@Data註解,它方便的使得我們不用手動構造getter、setter、equals、hashcode、toString、全量構造方法等方法。

@Data
public abstract class Operation {

    private Integer numA;
    private Integer numB;

    public abstract double getResult();

}

第二步,構建不同的操作方法類,構建加減乘除四種不同的方法,我以加法舉例

public class OperationAdd extends Operation {


    @Override
    public double getResult() {
        return super.getNumA() + super.getNumB();
    }


}

第三步:簡單工廠類

public class OperationFactory {

    public static Operation createOperation(String operate){

        Operation operation = null;
        switch (operate){
            case "+" : operation = new OperationAdd();break;
            case "-" : operation = new OperationSub();break;
            case "*" : operation = new OperationMul();break;
            case "/" : operation = new OperationDiv();break;
            default : new OperationAdd();
        }

        return operation;
    }
}
如上,一個簡單的工廠模式就構建完成了。

測試用例如下:

public class SimpleFactroyTest {

    @Test
    public void factoryTest(){
        Operation oper = OperationFactory.createOperation("+");
        oper.setNumA(1);
        oper.setNumB(2);
        System.out.println(oper.getResult());
    }
}

二、工廠方法模式

工廠方法模式,就是給工廠再進一步的抽象。這樣可以更靈活,擴充套件起來也是更加方便。


例項如下:

首先:有一個頂級工廠類,讓其他的類繼承此工廠類

public interface IFactory {
    
    Operation creatOperation();
    
}

其次:建立對應的工廠的子類

/**
 * 加法工廠
 * */
public class FactoryOperationAdd implements IFactory {

    /**
     * 加法操作
     * */
    @Override
    public Operation creatOperation() {
        return new OperationAdd();
    }

}

對應的工廠的子類去生產對應的操作方法,測試用類如下:

public class MethodFactoryTest {

    /**
     * 工廠方法模式
     * 可以更好的解耦。如果使用簡單工廠模式,多了一個開方的業務,我們需要對簡單工廠的程式碼進行修改
     * 而使用工廠方法模式,我們只需要再新建一個類去執行相關的業務就好了
     * 而不用原始碼進行修改。
     * */
    @Test
    public void methodTest(){
        IFactory iFactory = new FactoryOperationAdd();
        Operation operation = iFactory.creatOperation();
        operation.setNumA(1);
        operation.setNumB(2);
        System.out.println(operation.getResult());
    }
}

三、MyBatis中工廠模式的使用

在MyBatis中獲取DataSource時,我們將DataSource相關的資料封裝到MyBatis的配置檔案中,MyBatis在初始化時,會去讀取配置檔案中的資訊。Mybatis會根據使用者的在配置檔案的配置,動態的生成DataSource。生成Datasource的過程就使用了工廠模式。我們來看一下原始碼:

首先:有一個對工廠的抽象介面類

public interface DataSourceFactory {

  void setProperties(Properties props);

  DataSource getDataSource();

}

其次,根據不同型別的DataSource,建立不同的DataSourceFactory

最後:

通過不同的配置生成不同的DataSourceFactory,從而通過不同的DataSourceFactory建立不同型別的DataSource

rivate void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }
看到這裡工廠模式就結束了,對原始碼的分析一定要定焦。就是我想知道哪個部分是如何實現的,我就關心這一部分。這樣才能高效的讀懂原始碼,而不被原始碼拐偏。

如果有好奇的小夥伴,我們順路來分析一波

dataSourceElement
方法的實現:原始碼如下:
  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");  // 讀取XML中type的標籤
      Properties props = context.getChildrenAsProperties();  // 讀取子節點作為配置
       // 根據type新建一個DataSourceFatory的實際例子
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    } 
  } 

這時候我們不妨看看xml中Datasource是如何配置的:

 <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
  </dataSource>
再跟原始碼:
protected Class<?> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }
再往後還有一些細節,最終我們發現,它是從一張別名表,通過別名表裡的配置,去載入到相對應得類,而去建立物件的。
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);