第057篇 设计模式入门:单例、工厂、观察者的 Android 落地

简介: 设计模式本质是应对“变化”的解法:封装可变点,降低耦合。Android中,它源于真实痛点——如创建分散、行为需替换、状态需通知等。关键不在背23种名称,而在识别“哪处会因需求变更而反复修改”。单例防多实例、工厂解耦创建、策略隔离算法、观察者实现松耦合通信。用错的根源往往是“为模式而模式”,而非解决真实变化。

设计模式这题最尴尬的地方在于:答"是什么"人人能背,答"为什么"就开始分化。GoF 二十三个模式的名字能报一遍,但面试官真正想听的是——这个模式解决了什么变化带来的问题。Android 语境下,模式不是背出来的,是从"代码要改很多处"这类具体痛点里长出来的。

先把结论放在前面:设计模式是对反复出现的设计问题的解法总结,它解决的是"耦合"与"变化"两件事。三大分类在 Android 语境下各有典型:创建型解决"谁来做、怎么做",典型是工厂与单例;结构型解决"怎么组合",典型是适配器与装饰器;行为型解决"对象之间怎么协作、职责怎么分",典型是观察者与策略。判断该不该用模式的标准只有一个:这段代码是不是会因为变化而反复改。

机制拆解

拿最常被问的三个模式说清机制。单例保证一个类在进程内只有一个实例,核心是"私有构造 + 全局访问点",Android 里的天然落点就是 Application(进程内天然唯一)与各种 Manager 类。 工厂把"创建哪一类"从调用方挪到工厂类,调用方只依赖接口或类型参数,Android 里的落点是 ViewModelProvider.Factory、ViewModelProvider.AndroidViewModelFactory,以及各种解析器按类型注册后统一 create 的注册表写法。 观察者定义一对多的订阅关系,被观察者状态变化时通知所有订阅者,Android 里的落点遍布各处——LiveData/StateFlow、自定义的下载进度回调、Handler 配合的弱引用监听器集合,甚至 OnSharedPreferenceChangeListener。

策略模式是 Android 里性价比最高的一种:把"这块行为要能被替换"抽成一个接口,调用方持有接口引用而不是实现类。它和工厂常配合——策略解决"用哪个行为",工厂解决"造哪个对象"。 模板方法在 Android 里体现为 Activity/Fragment 的生命周期回调:框架把流程定好(onCreate → onStart → onResume),开发者填内容。装饰器对应各种流与包装:BufferedInputStream 包 InputStream、OkHttp 的 Interceptor 链层层包装原请求。

这些坑的正确绕法

最常见的坑是"为了用模式而用模式",把所有类都套上工厂接口,三层跳转只为创建一个对象。 表现是排查一个崩溃要从 Activity 一路点到工厂再点到实现,日志里全是工厂方法,调试成本陡增;更严重的是新增一个实现类要改五个文件。根因是把"变化点"当成了"现在就有需求"。判断方法是问自己:这个创建点现在有第二种实现吗?将来半年内会有吗?如果只有一个实现且短期不会变,直接 new 最清晰。

其次是把职责拆错,模式用对了但边界错了。 例如在 Activity 里既写界面逻辑又发网络请求又解析数据,无论怎么套模式都还是乱。Android 上的常见反倒是"过度分层":一个简单列表为了"可扩展"拆成 Model / DataSource / Repository / UseCase 四层,实际每层只有一个实现,层间层层转发,改一行要动五个文件。模式解决的是变化,不是层次。

还有一个更隐蔽的坑:单例持有 Context,内存泄漏。 static 字段持有 Activity 或 View 的引用会让整棵视图树无法回收,Activity 销毁后仍然驻留。 Android 上单例应持有 Application(或通过 getApplicationContext() 拿),需要绑定 Activity 时用弱引用 + 生命周期感知,或干脆改用 ViewModel 承载这类"活到界面结束"的状态。面试里能主动说出"单例不是万能的,作用域要匹配生命周期",就比只会背"私有构造 + 双重检查锁"高一档。

代码里见真章

看一段能直接跑的代码,把上面的机制落到具体写法上:

// 策略:把可替换的行为抽成接口,调用方只依赖接口
interface RetryPolicy {
    boolean shouldRetry(int attempt, IOException e); }
class ExponentialPolicy implements RetryPolicy {
   
    public boolean shouldRetry(int attempt, IOException e) {
   
        return attempt < 3 && !(e instanceof SSLException);   // 证书类不重试
    }
}
class Downloader {
                       // 只依赖接口,不感知具体实现
    private final RetryPolicy policy;
    Downloader(RetryPolicy policy) {
    this.policy = policy; }
}

// 工厂:注册表写法,按类型创建,避免每加一个就改一处
class ViewModelFactory implements ViewModelProvider.Factory {
   
    private final Map<Class<?>, Supplier<ViewModel>> registry = new HashMap<>();
    ViewModelFactory register(Class<?> type, Supplier<ViewModel> creator) {
   
        registry.put(type, creator); return this;      // 链式注册
    }
    @SuppressWarnings("unchecked")
    @Override public <T extends ViewModel> T create(Class<T> modelClass) {
   
        Supplier<ViewModel> s = registry.get(modelClass);
        if (s == null) throw new IllegalArgumentException("未注册: " + modelClass);
        return (T) s.get();
    }
}

// 观察者:保留强/弱引用要显式选择,否则泄漏
class DownloadListeners {
   
    private final List<WeakReference<Listener>> refs = new ArrayList<>();
    public void add(Listener l) {
   
        refs.add(new WeakReference<>(l));               // 弱引用避免泄漏界面
        if (refs.size() > 20) purge();                 // 顺手清掉已回收的
    }
    public void notifyProgress(int p) {
   
        for (Iterator<WeakReference<Listener>> it = refs.iterator(); it.hasNext();) {
   
            Listener l = it.next().get();
            if (l == null) it.remove(); else l.onProgress(p);
        }
    }
}

这段代码值得盯三处:第一处,RetryPolicy 把"能不能重试"的判断从 Downloader 里抽走,新增策略不需要动下载逻辑;第二处,工厂用注册表而不是 if-else 类型链,加一个 ViewModel 只需多一行注册;第三处,观察者列表用 WeakReference 并在通知时顺手清理 null,这是 Android 场景下的必要工程细节。

这题在面试里怎么问、怎么答

"设计模式解决了什么问题?"核心答一句话:封装变化,让依赖倒转——调用方依赖抽象而非实现,从而在实现替换时不影响调用方。 展开说:创建型把"谁创建"与"用什么创建"分开;结构型让组合方式可替换;行为型把"做什么"与"怎么做"分开。

"策略和工厂怎么配合?""单例和依赖注入冲突吗?""为什么 Kotlin 里少用单例?"三个高频追问。策略+工厂已在代码里演示。单例 vs DI:DI 容器管理生命周期与依赖关系,更易测试(可以注入假实现),单例只是"全局唯一"这一件事,混用时容易出现隐藏依赖,测试时无法替换。 Kotlin 少用单例是因为可以用 object 声明或把状态放进 ViewModel/StateFlow,作用域更明确。

"说一个你在项目里用设计模式的例子。"要讲清四段:痛点(哪段代码改起来很痛)、选型(为什么是这个模式不是别的)、代价(多了哪些文件与跳转、测试怎么写)、结果(改动收敛在某几个文件、回归范围变小)。只说"用了工厂模式"的答案会被追问穿。

"使用中遇到过什么问题?"案例一:某模块的支付实现从"直接 new"改成策略接口并加注册表,收益是新增第二家支付只改注册处,但代价是调试时要多看一层;结论是只为确定的会变点加抽象。案例二:一个静态工具类持有 Activity 导致连续打开详情页后内存不降;定位到静态字段捕获了界面上下文;修复为改为弱引用 + 生命周期感知回调。

再补一个工程上值得讲清的点:Android 有大量"框架已经替你实现了模式"的地方。Activity 生命周期是模板方法、RecyclerView.Adapter 是观察者、ListAdapter 加了 DiffUtil 是策略的体现、ViewPager2 的回调链是责任链。 识别出这些既省代码又少踩坑——ListAdapter 就是典型:自己实现 notifyItemRangeInserted 容易算错位置,而 DiffUtil 在后台线程算完差异再一次性刷新,这是框架替模式兜了坑。面试里能说出"哪些模式框架已经做了",比背二十三个名字更有价值。

给正在准备面试的你

这题可以画成一张"痛点 → 模式"映射图:左侧列五个真实痛点(对象创建太散、类型判断 if-else 越写越长、行为要可替换、状态变化要通知多处、层次过深看不清),右侧列对应的模式(工厂/单例、注册表+策略、策略+模板方法、观察者、组合优先),中间用虚线连接。虚线的粗细表示"该模式解决的痛点强度"——某些连线很细,说明那里其实不需要模式。面试中讲设计模式,图的右边通常要能画出来,左边才是加分点。

再补工程案例与踩坑——应用落点是把项目里 if (type == A) new A() else if (type == B) new B() 的类型分支收成注册表或策略接口,审查所有静态字段是否捕获了界面上下文,并为工厂类补上"未注册类型"这个分支的明确报错。

复习时别孤立刷题:单例模式六种写法——单例是创建型模式里最常被追问写法细节的那个。

划两句重点:模式解决的是"会变的地方",只为确定的变化点加抽象;策略抽行为、工厂管创建、观察者做通知;Android 上单例必须持 Application 而非界面上下文,优先用框架已经实现的模式。

下一篇聊单例模式六种写法:线程安全与懒加载的平衡——沿着今天这条主线继续往前走。


如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。你的支持就是这个系列持续更新的动力。

「Android软件开发面试·从入门到精通」连载系列

上一篇:新时间-API:LocalDateTime-取代-Date-的理由

下一篇预告:单例模式六种写法:线程安全与懒加载的平衡

有任何问题欢迎在评论区留言交流。

相关文章
|
17天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
8478 24
|
16天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
2817 14
|
15天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2021 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
14天前
|
人工智能 编解码 并行计算
MiniMax-H3 一键整合包技术文档:8G 显存运行 AI 漫剧制作 —— 角色替换 / 动作迁移 / 文图生视频部署与调参指南
MiniMax H3 是 MiniMax 开源的全模态视频生成模型,支持文/图/音/视多条件输入,输出最高2K、15秒带双声道音频视频。本文档详述其Int8量化版在8GB显存下的本地一键部署、三段式工作流(EDIT/REPLACE/CONTINUE)、参数调优及常见问题排查。(239字)
|
10天前
|
人工智能 Linux 开发者
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
Codex是OpenAI推出的AI编程智能体,可读取本地项目、理解需求并自动修改代码。支持桌面GUI、命令行(CLI)及VS Code/Cursor插件三种形态,覆盖可视化操作、终端高效开发与编辑器无缝集成场景,助开发者用自然语言驱动编码全流程。(239字)
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
|
4天前
|
人工智能 JSON Linux
【全网最详细】ComfyUI使用教程:下载+本地部署+配置+工作流搭建一篇搞定(2026最新版)
ComfyUI是一款免费开源的本地AI绘图工具,采用节点式工作流设计,支持文生图、图生图、局部重绘、放大、换脸等多种功能。可离线运行,依赖显卡加速,无需联网。支持自定义流程保存与分享,插件生态丰富,适合进阶用户。(239字)
|
10天前
|
人工智能 JSON 编解码
【2026最新版】ComfyUI本地部署教程,新手也能看懂!
ComfyUI是本地运行的AI绘画工具,采用节点式工作流设计:通过拖拽连接“加载模型”“提示词编码”“采样”“解码”等模块,实现高度可控的文生图。新手推荐使用秋叶整合包,一键启动、内置模型管理与插件安装器,轻松上手。(239字)

热门文章

最新文章