阿里P6设计模式面经:单例五种写法、观察者模式、策略模式、建造者模式

35 阅读6分钟

上篇算法聊完,这篇进入设计模式。P6面试考的不是"你知道几个模式",而是"Android源码里哪些地方用了?你在项目里怎么用?"

最常考四个:单例、观察者、策略、建造者。知道模式不难,难的是结合Android源码讲清楚。

今天8道题覆盖P6设计模式核心考点。

Q1:单例模式五种写法?各有什么问题?

饿汉式:类加载时创建实例,线程安全但可能资源浪费。private static final Singleton INSTANCE = new Singleton();

懒汉式(synchronized):延迟创建但性能差,每次获取都要锁。

双重检查锁(DCL):volatile + synchronized + 两次null检查,兼顾延迟和线程安全。

静态内部类:用内部类加载机制保证线程安全,代码简洁。getInstance时触发内部类加载。

枚举enum Singleton { INSTANCE; } 最简洁,防反射防序列化。Joshua Bloch推荐。

追问:Android里哪些是单例?InputMethodManager用DCL,AccessibilityManager用synchronized懒汉式,各种Manager基本都是单例。因为系统服务全局只需一个实例。

Q2:DCL单例为什么要volatile?不用会怎样?

对象创建分三步:分配内存 → 构造函数初始化 → 引用赋值。JVM可能把2和3重排。不用volatile的话,线程A赋值完毕但还没初始化,线程B进来instance不为null直接返回——拿到未初始化对象NPE。

volatile禁止2和3重排,保证instance不为null时对象一定初始化完成。DCL代码结构:外层null检查 → synchronized同步块 → 内层null检查 → instance = new Singleton()

追问:synchronized能代替volatile吗?不能。synchronized只能保证同一时刻只有一个线程进入同步块,不禁止指令重排。两个线程同时执行getInstance,一个在同步块内,另一个在外面做第一次null检查——外面的线程可能拿到未初始化对象。

Q3:观察者模式在Android里怎么用?

观察者模式定义一对多依赖,被观察对象状态改变时通知所有观察者。

LiveDataobserve(owner, observer)注册,setValue通知。自动处理生命周期——Activity destroy后取消观察,不泄漏。

BroadcastReceiver:sendBroadcast = 状态改变,Receiver = 观察者,IntentFilter = 订阅条件。

ContentObserverregisterContentObserver(uri, true, observer)注册,notifyChange(uri)触发onChange。

追问:LiveData和ObservableField区别?LiveData有生命周期感知能力(自动取消订阅),ObservableField没有——需要手动解绑。新项目推荐LiveData或StateFlow,别用ObservableField了。

Q4:策略模式是什么?Android哪里用了?

策略模式定义一组算法封装成类,运行时根据需要选择使用。消除大量if-else。

Interpolator(插值器):Android动画的Interpolator就是策略模式。LinearInterpolator线性变化、AccelerateInterpolator加速、DecelerateInterpolator减速、BounceInterpolator弹跳。都是TimeInterpolator接口的不同实现,ObjectAnimator根据设置的Interpolator选择动画曲线。

实际项目:支付方式选择——定义PayStrategy接口,AliPayStrategy/WechatPayStrategy各一个实现。用Map注册策略,strategyMap.get(payType).pay(order)替代if-else。

追问:策略模式和简单工厂什么区别?策略模式关注"封装算法",客户端选策略传给上下文。简单工厂关注"创建对象",客户端传参数工厂返回对象。实际项目经常结合用——工厂负责创建策略实例,策略模式负责执行。

Q5:建造者模式?Android哪里用了?

建造者模式把复杂对象的构建和表示分离,用链式调用逐步设置参数。

AlertDialog.Builder:最经典的例子。new AlertDialog.Builder(ctx).setTitle("退出").setMessage("确定?").setPositiveButton("确定", listener).show()。链式调用可读性好。

Retrofit/OkHttpClient:都用Builder配置。baseUrl、converter、timeout等参数链式设置,build()校验参数创建实例。

追问:Builder模式解决什么问题?当一个类有很多可选参数时,构造函数参数太多(超过4个就难读)。用Builder可以只设需要的参数,其他用默认值。而且链式调用可读性比长构造函数好很多。

Q6:工厂模式在Android里怎么用?

工厂模式封装对象创建过程,客户端不直接new而是通过工厂获取。

BitmapFactory:Android提供的工厂类。decodeFile(path)decodeResource(res, id)decodeStream(is)——不同来源创建Bitmap。客户端不用关心Bitmap怎么从文件/资源/流解码。

ViewModelProvider.Factory:ViewModel有参数构造时系统不能直接反射创建,需要自定义Factory实现create(Class<T>)方法返回ViewModel实例。

追问:简单工厂、工厂方法、抽象工厂区别?简单工厂是一个类根据参数返回不同对象。工厂方法是定义创建接口,子类决定创建什么。抽象工厂是创建一组相关对象(Android用得少)。面试考得最多的是简单工厂和工厂方法。

Q7:适配器模式?RecyclerView.Adapter怎么理解?

适配器模式把不兼容的接口转成可用的接口。

RecyclerView.Adapter:数据源和RecyclerView显示接口不兼容。Adapter做桥梁——onCreateViewHolder创建视图,onBindViewHolder绑定数据,getItemCount返回条数。

InputStreamReader:InputStream(字节流)和Reader(字符流)不兼容,InputStreamReader做适配。

追问:Adapter和Decorator什么区别?Adapter改接口(让不兼容变兼容),Decorator加功能(不改变原有接口)。RecyclerView.Adapter是Adapter——它把数据源接口适配成RecyclerView需要的接口。

Q8:项目中怎么综合运用设计模式?

MVVM架构里ViewModel持有LiveData(观察者模式),Repository根据网络状态选数据源(策略模式),Retrofit用Builder配置(建造者模式),列表展示用Adapter(适配器模式)。一个App里至少用到十几种设计模式。

追问:面试问"你项目里用了什么设计模式"怎么答?不要报菜名。要讲具体场景——"支付模块用策略模式封装支付方式,工厂根据用户选择创建策略,结果用LiveData通知UI"。场景+模式+效果,三句话讲清楚。


面试Tips:P6设计模式面试不是考名词解释,是考你能不能在Android源码里找到模式并讲清楚。单例考DCL为什么用volatile。观察者考LiveData的生命周期感知。策略考Interpolator。建造者考AlertDialog.Builder。工厂考BitmapFactory。回答设计模式问题一定要结合Android实际代码,纯讲理论拿不到P6 offer。

下一篇深入Java并发专项——AQS内部结构、ReentrantLock的Condition机制、CountDownLatch和CyclicBarrier区别。


项目里用设计模式最多的模块是什么?评论区说说你的经验。

本系列连载中,关注不迷路,下一篇:阿里P7高级Android(Java并发专项)面试真题

系列简介:Android大厂面经连载,覆盖字节跳动、腾讯、阿里、美团等40+企业,从初级到架构师全岗位覆盖。每篇文章包含真实面试题+详细答案+代码示例,帮你拿到大厂Offer。