5.Android 设计模式 五个核心之二:工厂模式 在项目中的实战

185 阅读10分钟

1.概念

核心思想: 不关心实现,只要给配置,得到结果! 定义统一引擎的方法,让它们各自实现

将对象的创建逻辑与使用逻辑分离。客户端无需关心对象的具体实现,只需通过工厂接口获取所需对象。

  • 类型

    • 简单工厂:一个静态方法创建所有对象(非严格设计模式)
    • 工厂方法:定义抽象工厂接口,子类决定实例化哪个类
    • 抽象工厂:创建相关对象族(如不同UI风格控件)

注意: 创建对象有很多种方式,反射也是,所以下面也是工厂模式!

// 运行时实例化拦截器
IInterceptor interceptor = interceptorClass.newInstance(); // 工厂模式创建实例

这行代码借助反射机制来创建对象实例,就像是一个简单工厂,依据传入的类名生成对应的对象。

2.在Android源码中的应用场景

2.1 BitmapFactory.decodeXxx()

根据资源/流类型自动创建Bitmap对象,隐藏解码细节:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img);
2. 2 LayoutInflater

模式类型:工厂方法模式

作用:解析XML布局文件,根据标签名(如 <TextView>)创建对应的View对象。子类可通过 setFactory() 自定义View创建逻辑(如换肤功能)。

源码调用

    View view = LayoutInflater.from(context).inflate(R.layout.item_view, parent, false);

3.UML图

微信截图_20250702222931.png

4.语音项目的例子和没用设计模式的对比

场景:处理不同云端语音识别引擎(讯飞语音/百度语音)
4.1 没有使用工厂模式的实现
// 讯飞实现类(需了解具体构造细节)
public class IflytekRecognizerWithout {
    private String appId;

    public void setAppId(String id) { this.appId = id; }
    public void connectServer() { /* 建立讯飞专有连接 */ }
    public String recognize(byte[] data) {
        // 调用讯飞SDK
        return "Iflytek:识别结果";
    }
}
// 百度实现类(接口不一致)
public class BaiduRecognizerWithout {
   private String apiKey;

   public void setApiKey(String key) { this.apiKey = key; }
   public void authToken() { /* 百度鉴权流程 */ }
   public String processAudio(byte[] data) {
       // 调用百度SDK
       return "Baidu:识别结果";
   }
}
// 客户端代码直接依赖具体实现类
public class SpeechClient {

    // ==== 使用示例 ====
    public static void main(String[] args) {
        SpeechClient client = new SpeechClient();
        byte[] audioData = null;

        // 调用讯飞(配置参数可抽离到配置文件)
        client.recognize("Iflytek", "APP_ID=123456", audioData);

        // 调用百度(客户端代码无需修改)
        client.recognize("Baidu", "API_KEY=7890abc", audioData);
    }

    public void recognize(String engineType, byte[] audioData) {
        if ("Iflytek".equals(engineType)) {
            // 直接实例化讯飞引擎
            IflytekRecognizerWithout iflytek = new IflytekRecognizerWithout();
            iflytek.setAppId("your_iflytek_id");
            iflytek.connectServer();
            String result = iflytek.recognize(audioData);
            System.out.println("讯飞结果: " + result);

        } else if ("Baidu".equals(engineType)) {
            // 直接实例化百度引擎
            BaiduRecognizerWithout baidu = new BaiduRecognizerWithout();
            baidu.setApiKey("your_baidu_key");
            baidu.authToken();
            String result = baidu.processAudio(audioData);
            System.out.println("百度结果: " + result);

        } else {
            throw new IllegalArgumentException("不支持的引擎");
        }
    }

    private final RecognizerFactory factory = new RecognizerFactory();

    public void recognize(String engineType, String config, byte[] audioData) {
        // 通过工厂创建实例(不感知具体类)
        SpeechRecognizer recognizer = factory.createRecognizer(engineType);

        // 统一接口操作
        recognizer.initialize(config);
        String result = recognizer.recognize(audioData);

        System.out.println(engineType + "结果: " + result);
    }
}
4.2 使用工厂模式的实现
// ===== 1. 统一定义产品接口 =====
public interface SpeechRecognizer {
    void initialize(String config); // 统一初始化
    String recognize(byte[] audioData); // 统一识别方法
}
public class BaiduRecognizer implements SpeechRecognizer {
    private String apiKey;

    @Override
    public void initialize(String config) {
        this.apiKey = config;
        this.authToken();
    }

    private void authToken() { /* 百度鉴权 */ }

    @Override
    public String recognize(byte[] audioData) {
        // 调用百度SDK(实际代码)
        return "Baidu:识别结果";
    }
}
// ===== 2. 实现具体产品 =====
public class IflytekRecognizer implements SpeechRecognizer {
    private String appId;

    @Override
    public void initialize(String config) {
        this.appId = config;
        this.connectServer();
    }

    private void connectServer() { /* 讯飞专有连接 */ }

    @Override
    public String recognize(byte[] audioData) {
        // 调用讯飞SDK(实际代码)
        return "Iflytek:识别结果";
    }
}
public class AliyunRecognizer implements SpeechRecognizer{


    @Override
    public void initialize(String config) {

    }

    @Override
    public String recognize(byte[] audioData) {
        return "";
    }
}
// ===== 3. 工厂类封装创建逻辑 =====
public class RecognizerFactory {
    public SpeechRecognizer createRecognizer(String engineType) {
        switch (engineType) {
            case "Iflytek":
                return new IflytekRecognizer();
            case "Baidu":
                return new BaiduRecognizer();
            // 新增引擎只需扩展此处
            case "Aliyun":
                return new AliyunRecognizer();
            default:
                throw new IllegalArgumentException("未知引擎类型");
        }
    }
}

4.3  扩展新引擎实战(阿里云)

只需新增两个类,无需修改已有客户端代码

    // 1. 实现产品接口
public class AliyunRecognizer implements SpeechRecognizer {
    @Override
    public void initialize(String config) {
        // 阿里云特有的初始化
    }
    
    @Override
    public String recognize(byte[] audioData) {
        return "Aliyun:识别结果";
    }
}

// 2. 扩展工厂类
public class RecognizerFactory {
    public SpeechRecognizer createRecognizer(String engineType) {
        switch (engineType) {
            // ... 已有引擎
            case "Aliyun":  // 新增分支
                return new AliyunRecognizer();
            default: ...
        }
    }
}
4.4 处理不同云端语音识别引擎的架构图

deepseek_mermaid_20250702_dd6940.png

✅ 工厂模式的核心优势

维度无工厂模式工厂模式
耦合度客户端与具体引擎强耦合客户端只依赖抽象接口
扩展性新增引擎需修改所有调用点只需扩展工厂类,符合开闭原则
代码复用重复初始化逻辑(每个引擎单独处理)初始化逻辑封装在具体产品类中
维护成本修改引擎需全局搜索调用代码只需修改具体产品类或工厂类
接口规范不同引擎接口不一致通过接口强制统一行为

5.优点

  1. 解耦:对象创建与使用分离,降低模块耦合度
  2. 可扩展性:新增产品类型只需扩展工厂,符合开闭原则
  3. 代码复用:集中管理对象创建逻辑(如配置初始化)
  4. 可维护性:修改具体类不影响调用方(如替换Google SDK为阿里云SDK)

6.和相识的设计模式的区别

工厂模式和哪个模式比较像呢? 策略模式

对比下面2份代码,是不是发现都是if else 的结构!他们各自到底用的什么设计模式!

Arounter中的源码: 策略模式

if (obj instanceof IRouteRoot) {
    registerRouteRoot((IRouteRoot) obj);
} else if (obj instanceof IProviderGroup) {
    registerProvider((IProviderGroup) obj);
} else if (obj instanceof IInterceptorGroup) {
    registerInterceptor((IInterceptorGroup) obj);
} else {
    logger.info(TAG, "register failed, class name: " + className
            + " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.");
}

上面的语音项目的例子:工厂模式

public SpeechRecognizer createRecognizer(String engineType) {
    switch (engineType) {
        case "Iflytek":
            return new IflytekRecognizer();
        case "Baidu":
            return new BaiduRecognizer();
        // 新增引擎只需扩展此处
        case "Aliyun":
            return new AliyunRecognizer();
        default:
            throw new IllegalArgumentException("未知引擎类型");
    }
}
6.1 相同点: 条件分发的核心结构相同
    // 策略模式的条件分支
if (obj instanceof IRouteRoot) {
    // 处理路由表
} else if (obj instanceof IProviderGroup) {
    // 处理服务
}

// 工厂模式的条件分支
switch (engineType) {
    case "Iflytek": return new IflytekRecognizer();
    case "Baidu": return new BaiduRecognizer();
}
  • 都使用 条件判断(if-else 或 switch-case)作为核心分发机制
6.1.2 不同点分析:

策略模式特征分析:

  1. 核心目的行为选择 - 根据输入对象的类型选择不同的处理算法

  2. 关注点运行时行为决策(如何处理对象)

  3. 模式结构

    • 多种策略接口:IRouteRootIProviderGroupIInterceptorGroup

    • 策略实现:各种注册方法(registerRouteRootregisterProvider等)

工厂模式特征分析:

  1. 核心目的对象创建 - 封装对象的实例化过程

  2. 关注点创建何种对象(而非如何处理)

  3. 模式结构

    • 抽象产品接口:SpeechRecognizer

    • 具体产品:IflytekRecognizerBaiduRecognizer

    • 工厂类:封装创建逻辑

核心区别对比

维度策略模式工厂模式
主要目的选择算法,不同的实现创建对象实例
核心关注点"怎么做"(行为实现)"是什么"(对象类型)
输入输出不同的接口相同的接口
6.2 简单工厂, 工厂方法, 抽象工厂 它们的区别是什么,用一个demo说明下

6.2.1 简单工厂模式 (Simple Factory)

核心特点:一个工厂类创建所有类型产品

    // 产品接口
interface Button {
    void render();
}

interface TextField {
    void display();
}

// 具体产品
class LightButton implements Button {
    public void render() { System.out.println("渲染浅色按钮"); }
}

class DarkButton implements Button {
    public void render() { System.out.println("渲染深色按钮"); }
}

class LightTextField implements TextField {
    public void display() { System.out.println("显示浅色文本框"); }
}

class DarkTextField implements TextField {
    public void display() { System.out.println("显示深色文本框"); }
}

// 简单工厂 - 创建所有UI组件
class SimpleUIFactory {
    public static Button createButton(String theme) {
        return switch (theme) {
            case "light" -> new LightButton();
            case "dark" -> new DarkButton();
            default -> throw new IllegalArgumentException("未知主题");
        };
    }

    public static TextField createTextField(String theme) {
        return switch (theme) {
            case "light" -> new LightTextField();
            case "dark" -> new DarkTextField();
            default -> throw new IllegalArgumentException("未知主题");
        };
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        Button btn = SimpleUIFactory.createButton("light");
        TextField field = SimpleUIFactory.createTextField("light");
        
        btn.render();       // 输出:渲染浅色按钮
        field.display();    // 输出:显示浅色文本框
    }
}

缺点:新增主题需要修改工厂类(违反开闭原则)

6.2.2. 工厂方法模式 (Factory Method)

核心特点:每个产品有专属工厂,通过子类决定实例化

    // 抽象工厂接口
interface ButtonFactory {
    Button createButton();
}

interface TextFieldFactory {
    TextField createTextField();
}

// 具体工厂
class LightButtonFactory implements ButtonFactory {
    public Button createButton() { return new LightButton(); }
}

class DarkButtonFactory implements ButtonFactory {
    public Button createButton() { return new DarkButton(); }
}

class LightTextFieldFactory implements TextFieldFactory {
    public TextField createTextField() { return new LightTextField(); }
}

class DarkTextFieldFactory implements TextFieldFactory {
    public TextField createTextField() { return new DarkTextField(); }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        ButtonFactory btnFactory = new LightButtonFactory();
        TextFieldFactory fieldFactory = new LightTextFieldFactory();
        
        Button btn = btnFactory.createButton();
        TextField field = fieldFactory.createTextField();
        
        btn.render();       // 输出:渲染浅色按钮
        field.display();    // 输出:显示浅色文本框
    }
}

缺点:组件之间没有约束(可混用浅色按钮+深色文本框)

6.2.3. 抽象工厂模式 (Abstract Factory)

核心特点:一个工厂创建整套相关产品,确保产品兼容性

    // 抽象工厂接口 - 创建产品族
interface UIFactory {
    Button createButton();
    TextField createTextField();
}

// 具体工厂 - 创建整套浅色组件
class LightThemeFactory implements UIFactory {
    public Button createButton() { return new LightButton(); }
    public TextField createTextField() { return new LightTextField(); }
}

// 具体工厂 - 创建整套深色组件
class DarkThemeFactory implements UIFactory {
    public Button createButton() { return new DarkButton(); }
    public TextField createTextField() { return new DarkTextField(); }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        // 选择整套主题
        UIFactory factory = new LightThemeFactory(); 
        
        Button btn = factory.createButton();
        TextField field = factory.createTextField();
        
        btn.render();       // 输出:渲染浅色按钮
        field.display();    // 输出:显示浅色文本框
        
        // 切换主题只需替换工厂
        factory = new DarkThemeFactory();
        btn = factory.createButton();   // 深色按钮
        field = factory.createTextField(); // 深色文本框
    }
}

deepseek_mermaid_20250702_f8147d.png

通过上面的列子,对比他们的区别

维度简单工厂 (Simple Factory)工厂方法 (Factory Method)抽象工厂 (Abstract Factory)
工厂数量1个万能工厂每个产品1个工厂每个产品族1个工厂
扩展性修改工厂类 (违反开闭原则)新增产品需新增工厂类 (符合开闭原则)新增产品族需新增工厂类 (符合开闭原则)
核心差异一个工厂类创建所有对象 (静态方法)抽象工厂接口,子类决定实例化哪个类创建多个相关对象族 (如整套UI组件)
产品关系独立创建产品独立创建产品创建相关产品族 (强制兼容性)
Android示例BitmapFactory.decodeResource()LayoutInflater的不同实现类跨平台UI组件库 (Material vs Cupertino)
适用场景产品类型少且固定产品类型多且可能扩展需要确保产品兼容性的系统
创建方式静态方法+条件分支工厂子类重写创建方法工厂子类实现整套创建方法
复杂度⭐☆☆☆☆ (简单)⭐⭐☆☆☆ (中等)⭐⭐⭐⭐⭐ (复杂)
客户端依赖依赖具体工厂类依赖具体工厂类只依赖抽象工厂接口

3者的架构图

deepseek_mermaid_20250702_fa7dbb.png

总结: 通过上表可见,Android 系统对工厂模式的应用集中在 封装复杂对象的创建逻辑 与 支持动态扩展 两大方向,其核心价值在于:

  1. 降低耦合:客户端无需依赖具体类(如 Bitmap 的实现类),只需面向接口编程。
  2. 简化扩展:新增产品类型(如新的日志器或View类型)只需扩展工厂,无需修改客户端代码。
  3. 统一入口:集中管理对象创建逻辑(如 LayoutInflater 统一处理所有View的实例化)。

💡 规律总结:当遇到以下场景时,Android源码优先采用工厂模式:

  • 对象创建依赖外部参数(如图片源、布局类型)
  • 需支持多态或动态替换实现(如网络库、日志系统)
  • 构造过程复杂(如Bitmap解码需处理多种输入源) 在Android中,工厂模式尤其适合需要动态切换实现类的场景(如网络库、音视频模块、多引擎支持)。通过将变化封装在工厂中,显著提升代码应对需求变更的能力。

项目工程的地址: github.com/pengcaihua1…