定义
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口
无须指定它们具体的类
类型
创建型
适用场景
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属于同一产品族)一起使用,创建对象需要大量重复的代码
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体的实现
优点
- 具体的产品在应用层代码隔离,无须关心创建细节
- 将一系列的产品族统一到一起创建
缺点
- 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
- 增加了系统的抽象性和理解难度
产品等级结构与产品族
工厂方法模式针对的是产品等级结构,抽象工厂模式针对的就是产品族,我们只要在美的工厂里取空调,取出来的肯定是美的的空调,在美的工厂里取冰箱,取出来的肯定也是美的冰箱。所以,我们只要指出一个产品所处的产品族,以及所属的等级结构,就可以唯一确定这个产品。
这里一定要注意抽象工厂和工厂方法最大的区别,具体使用哪一个还是要看我们实际的业务场景。理论上来说,当一个工厂可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,那这个时候抽象工厂模式比工厂方法模式更为简单更有效率。
coding
业务场景:现在有一个新的要求,每一个课程不仅要有视频,还要有对应的手记。
那如果按照工厂方法的方式来扩展的话,我们想象一下:前端手机、java手记都是各有特色,那如果按照工厂方法现在的扩展来说,我们要创建一个前端的手记类、java的手记类、python的手记类,前端手记的工厂、java手记的工厂、python手记的工厂,同时我们还要创建手记的抽象类,还有手记的工厂。
在工厂方法中,如果像这种我们的业务场景发生了比较大的扩展,很容易发生类爆炸这么一种情况,也就是说我们要建很多很多的类。
而原来呢一个视频就是一个课程,现在是一个视频+手记是一个课程,现在分析下,java视频、前端视频、python视频属于同一产品等级,java手记、前端手记、python手记都是同一产品等级;java视频+java手记属于同一产品族
抽象工厂,使用接口、抽象类都行
public interface CourseFactory {
Video getVideo();
Article getArticle();
}
手记
public abstract class Article {
public abstract void produce();
}
视频
public abstract class Video {
public abstract void produce();
}
视频+手记是一个课程,下面声明一个java产品族的工厂
public class JavaCourseFacotry implements CourseFactory {
@Override
public Video getVideo() {
return new JavaVideo();
}
@Override
public Article getArticle() {
return new JavaArticle();
}
}
java手记
public class JavaArticle extends Article{
@Override
public void produce() {
System.out.println("编写Java课程手记");
}
}
java视频
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制java视频课程");
}
}
视频+手记是一个课程,下面声明一个python产品族的工厂
public class PythonCourseFacotry implements CourseFactory {
@Override
public Video getVideo() {
return new PythonVideo();
}
@Override
public Article getArticle() {
return new PythonArticle();
}
}
python手记
public class PythonArticle extends Article{
@Override
public void produce() {
System.out.println("编写Python课程手记");
}
}
python产品族
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python视频课程");
}
}
类图
每个设计模式都是结合实际的业务场景,在某些业务场景可能这种设计模式就要改进,甚至优化,甚至可能带来更多的麻烦。例如我们现在要增加算法课程工厂,对于现在的抽象工厂模式非常的容易扩展,对原来的类还不需要变化,也就是说,我们在这里创建一个算法的课程工厂,它实现课程工厂接口,接着再创建具体的算法视频、算法手记这两个实际的产品就可以了,其它的类是不需要动的。
应用层
public class Test {
public static void main(String[] args) {
CourseFactory courseFactory=new JavaCourseFacotry();
Video video=courseFactory.getVideo();
Article article=courseFactory.getArticle();
video.produce();
article.produce();
}
}
看上面应用层代码,在使用时候并不关心产品族创建的一个过程,我们应用层代码只需要关心它是那个产品族工厂就可以了,因为我们应用层代码不会直接创建Java手记和python手记,java视频和python视频,整个创建过程是交由具体的工厂来创建的,并且它创建的是一个产品族产品,这里边创建出来的肯定都是java的。
这也是抽象工厂的一大优点:
- 应用层代码不和具体的产品发生依赖,只和具体的产品族工厂发生依赖关系
- 从具体的产品族工厂取出来的肯定是同一产品族
- 扩展性好:比如新增 前端的产品族,只需要增加前端的课程工厂和前端的具体产品就可以了,很方便,无需修改现有系统,符合开闭原则。
抽象工厂的缺点是新增产品等级比较麻烦,要对原有的系统进行比较大的修改,在这种业务场景下就违背了开闭原则,所以说某些原则在某些场景是符合的,比如扩展产品族,这个是符合开闭原则的;但是在产品族里为现在有的产品增加新的产品等级,那就不符合开闭原则了。比如:
我们引入一个新的业务场景,这个课程呢除了视频、手记,还要将源码放在里面,也就是说视频、手记、源码才能构成课程,那么对原有的修改就比较大了。首先对课程工厂增加一个源码,再对具体的课程工厂增加源码,最后再创建源码的抽象类和写具体的java源码、python源码的实际产品,这时候是不符合开闭原则的,因为我们要修改具体的实现了。
所以对于产品族里扩展新的产品等级还是比较麻烦的,所以我们在平时工作的业务场景会找一些产品等级结构相对固定的,并且还需要多个产品来组合到一起形成产品族的这么一个业务场景,当然如果碰到频繁改动也不适于抽象工厂,比如今天加一个产品等级,明天还要加一个,后天还要加一个,这个是太频繁了,这个类要经常经常改。我们要找相对固定的,比如可以固定个一年两年三年的,这个时间就很长了,这种我们去改也是ok的,只不过测试的时候多仔细些。比如随着网站不断的扩展,一年两年呢增加一个新的产品等级也是没有问题的,半年也是ok的。
源码解析
java.sql.Connection
它是一个接口,所以方法也都是抽象方法,看下面这两个方法
Statement createStatement() throws SQLException;
PreparedStatement prepareStatement(String sql)
throws SQLException;
很明显这两个方法返回的都是同一个产品族,例如MySQL的、PostgreSQL的。再看方法返回值Statement
,
也是用到抽象工厂。再看Connection的实现类
我们只要从MySQL这个实现类中获取,得到的一定是MySQL这个产品族中的东西。
org.apache.ibatis.session.SqlSessionFactory
package org.apache.ibatis.session;
import java.sql.Connection;
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean var1);
SqlSession openSession(Connection var1);
SqlSession openSession(TransactionIsolationLevel var1);
SqlSession openSession(ExecutorType var1);
SqlSession openSession(ExecutorType var1, boolean var2);
SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);
SqlSession openSession(ExecutorType var1, Connection var2);
Configuration getConfiguration();
}
这个接口不仅返回SqlSession,还要返回Configuration,那很明显只要通过SqlSessionFactory去获取的这两种类型,都是同一种产品族。
再看一下实现类
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;
}
定义的是返回一个SqlSession,实际返回的是一个DefaultSqlSession。