篇章目标要点
日常开发工作中,新入职场的童鞋对于接口Interface的最重要的认识就是用于异步通信,可以用于数据回调。今天我想结合Glide源码阅读中发现的接口Interface用途,引出今天的话题抽象工厂模式的用法。在介绍抽象工厂模式之前,先介绍下简单工厂模式的常见用法,随着展开抽象工厂模式的用法,优缺点分析,最后再结合Glide源码强化一下这种设计模式的运用。
简单工厂模式
定义:由一个工厂对象决定创建出哪一种产品类的实例。适用于创建一群相关类,如以下结构示例,一个用于生产形状的基类。被子类继承后可以生产圆形,生产矩形,生产三角形。
产品基类
//画形状
class Shape{
void drawStroke(){
...
}
void drawSolid(){
...
}
}
子类继承后分别生产圆形/生产矩形/生产三角形,示例如下
//画圆
class OvalShape extends Shape{
void drawStroke(){
...
}
void drawSolid(){
...
}
}
生产矩形
//画矩形
class RectShape extends Shape{
void drawStroke(){
...
}
void drawSolid(){
...
}
}
生产三角形
//画三角形
class TriangleShape extends Shape{
void drawStroke(){
...
}
void drawSolid(){
...
}
}
为了避免对绘制形状产品对象的创建过程的关注,一般会创建以下形式的管理类,在该类中通过识别传入工厂模式的参数来决定创建哪种产品对象。
管理的工厂模式
class class ShapeFactory{
public static Shape createShape(String type){
Shape shape = null;
switch(type){
case "oval":
shape = new OvalShape();
break;
case "rect":
shape = new RectShape();
break;
case "trianglle":
shape = new TriangleShape();
break;
}
return shape;
}
}
抽象工厂模式
定义:仅创建一个用于创建产品对象的接口或者抽象类,让子类实现该接口,根据实际使用创建相应的子类。其结构的基本形式如下
创建产品对象的接口如下
//画形状
interface Shape{
void drawStroke();
void drawSolid();
}
实现接口,形成创建生产圆形/生产矩形/生产三角形的对象。
//画圆
class OvalShape implements Shape{
void drawStroke(){
...
}
void drawSolid(){
...
}
}
//画矩形
class RectShape implements Shape{
void drawStroke(){
...
}
void drawSolid(){
...
}
}
//画三角形
class TriangleShape implements Shape{
void drawStroke(){
...
}
void drawSolid(){
...
}
}
如以下所述,抽象工厂的运用不必罗列全部产品对象的创建过程
class ShapeFactory{
private static Shape shape = null;
//创建产品对象
public static void createShape(Shape shape){
this.shape = shape;
}
//执行不关注
public static void setDrawStroke(){
shape.drawStroke();
}
}
//创建圆形产品对象和执行方法
ShapeFactory.createShape(new OvalShape());
ShapeFactory.setDrawStroke();
//创建矩形产品对象和执行方法
ShapeFactory.createShape(new RectShape());
ShapeFactory.setDrawStroke();
两种模式的优缺点
项目 | 简单工厂模式 | 抽象工厂模式 |
---|---|---|
应用场景 | 创建的对象种类比较少时 | 当所创建的对象的产品接口类别稳定,不会变更的情况 |
优点 | 外界不需要了解对象创建的过程,外界只需要输出工厂类的参数即可 | 1.对子类的共同产品接口进行定义,不必设置一个工厂类负责全部子类创建过程的管理2.对外暴露的只是定义的接口,不会暴露内部实现逻辑,实现了代码的解耦 |
缺点 | 1.拓展性差,增加子类还需要修改工厂方法增加分支条件,违背了开放封闭原则;2.对象种类增加之后,代码维护困难 | 如果生产的产品接口有发生变化,那么全部子类都要进行调整 |
抽象工厂模式在Glide源码的应用
在Glide源码中DecodeJob类中有一个片段代码目的是根据任务类型来决定是使用从硬盘读取转换后的资源/从硬盘读取转换前的资源/网络请求资源,其采用的就是这种抽象工厂模式
调用的用法如下
private volatile DataFetcherGenerator currentGenerator;
//2.创建任务执行器
currentGenerator = getNextGenerator();
//2.创建任务执行器
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE://从硬盘缓存中读取转换后数据
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE://从硬盘缓存中读取未转换的原始数据
return new DataCacheGenerator(decodeHelper, this);
case SOURCE://请求加载数据
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
其定义的抽象接口如下
interface DataFetcherGenerator {
interface FetcherReadyCallback {
void reschedule();
void onDataFetcherReady(
Key sourceKey,
@Nullable Object data,
DataFetcher<?> fetcher,
DataSource dataSource,
Key attemptedKey);
void onDataFetcherFailed(
Key attemptedKey, Exception e, DataFetcher<?> fetcher, DataSource dataSource);
}
boolean startNext();
void cancel();
}
看看读取硬盘转换后的资源的产品类ResourceCacheGenerator实现过程如下
class ResourceCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {
...
ResourceCacheGenerator(DecodeHelper<?> helper, FetcherReadyCallback cb) {
this.helper = helper;
this.cb = cb;
}
// See TODO below.
@SuppressWarnings("PMD.CollapsibleIfStatements")
@Override
public boolean startNext() {
...
return started;
} finally {
GlideTrace.endSection();
}
}
private boolean hasNextModelLoader() {
return modelLoaderIndex < modelLoaders.size();
}
@Override
public void cancel() {
LoadData<?> local = loadData;
if (local != null) {
local.fetcher.cancel();
}
}
@Override
public void onDataReady(Object data) {
cb.onDataFetcherReady(
sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, currentKey);
}
@Override
public void onLoadFailed(@NonNull Exception e) {
cb.onDataFetcherFailed(currentKey, e, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE);
}
}
另外两个产品类也是相同的做法,在这儿就不列举了。
学习心得
抽象工厂模式适用于产品接口类别稳定的情况,基于该模式可以实现逻辑和调用之间的解耦,仅仅将接口暴露给调用方。该模式不必设置一个工厂类负责全部子类产品对象的创建过程,有着较好的拓展性。但是没有一种方式是完美的,只要适合项目需要就是好的。