簡介
介紹抽象工廠之前,需要先介紹兩個概念:產品族和產品等級。產品族是指同一主題的產品,例如Java的錄像和Java的文章都屬於Java主題的產品,他們將在同一個工廠生產。產品等級是指不同產品族中的同類產品,比如Java的錄像和Python的錄像,他們分屬不同的主題,但都是錄像這種類型。
抽象工廠可以將一個產品族中的產品(多個產品等級)統一到一起創建。因此在抽象工廠創建時就決定了其包含的產品等級,所以在產品等級需要擴充或修改時,就需要改動抽象工廠,不滿足開閉原則。
舉例
我們將工廠方法中的例子繼續擴展,假如現在需要創建一門課程(產品族),而一門課程必須包含視頻(產品等級)和文章(產品等級)兩部分。
首先需要定義抽象的課程工廠、視頻和文章。
public abstract class CourseFactory {
public abstract Video getVideo();
public abstract Article getArticle();
}
public abstract class Video {
public abstract void produce();
}
public abstract class Article {
public abstract void produce();
}
然後根據業務需要和已有的抽象規範,我們可以創建不同的產品。
public class JavaVideo extends Video {
public void produce() {
System.out.println("This is Java Video.");
}
}
public class PythonVideo extends Video {
public void produce() {
System.out.println("This is Python Video.");
}
}
public class JavaArticle extends Article {
public void produce() {
System.out.println("This is Java Article.");
}
}
public class PythonArticle extends Article {
public void produce() {
System.out.println("This is Python Article.");
}
}
對於不同的產品族,我們分別創建不同的工廠。
public class JavaCourseFactory extends CourseFactory {
public Video getVideo() {
return new JavaVideo();
}
public Article getArticle() {
return new JavaArticle();
}
}
public class PythonCourseFactory extends CourseFactory {
public Video getVideo() {
return new PythonVideo();
}
public Article getArticle() {
return new PythonArticle();
}
}
到此,應用層就可以通過不同的創建不同的工廠,直接獲取需要的產品,而無需關心和重複編寫具體產品的實現細節。
public class Test {
public static void main(String[] args) {
CourseFactory courseFactory = new JavaCourseFactory();
Video video = courseFactory.getVideo();
video.produce();
Article article = courseFactory.getArticle();
article.produce();
}
}
MyBatis中的抽象工廠
SqlSession是MyBatis工作的核心,MyBatis在創建SqlSession和Configuration對象時,使用的就是抽象工廠,保證了調用者從一個工廠對象中取出的SqlSession和Configuration是配套的。比如MySQL工廠中取出的就是MySQL的SqlSession和Configuration,Oracle工廠中取出的就是Oracle的SqlSession和Configuration。
public interface SqlSessionFactory {
... ...
SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);
Configuration getConfiguration();
}
例如實現了SqlSessionFactory接口的DefaultSqlSessionFactory,openSession方法中實際返回了DefaultSqlSession,getConfiguration也會返回對應的配置。應用層也無需關心DefaultSqlSession的複雜實現。
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
return this.openSessionFromDataSource(execType, level, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
public Configuration getConfiguration() {
return this.configuration;
}
... ...
}
總結
和工廠方法相比,抽象工廠可以提供成套的、分屬不同產品等級的產品。如果一個工廠需要並且可以創建出分屬於不同產品等級的統一產品族的產品時,抽象工廠比工廠方法更加簡潔且有效率。要分析業務場景是否需要在同一產品族中劃分不同產品等級,來決定使用抽象工廠還是工廠方法。