4、平台抽象层设计:抽象层的作用、如何设计平台无关接口、接口隔离原则、依赖注入
好,咱们今天聊聊平台抽象层。说实话,这是跨平台开发里最核心的一环,也是我踩坑最多的地方。
你想想看,我们做跨平台,最怕什么?最怕代码写死了,换个平台就得重写。抽象层就是来解决这个问题的——它像一层“缓冲垫”,把业务逻辑和平台细节隔开。
抽象层到底在干什么?
说白了,抽象层就是定义一套“接口契约”。
业务层只跟这套契约打交道,不关心底层是 iOS 还是 Android,是 Windows 还是 Linux。底层怎么实现?那是抽象层实现类的事。
举个例子。我在项目中做过一个文件读写模块。业务层只需要调用 saveFile() 和 readFile(),至于底层是用 NSFileManager 还是 java.io.File,业务层完全不知道。这就是抽象层的作用——解耦。
如何设计平台无关接口?
设计平台无关接口,我个人习惯遵循几个原则:
- 接口要小,职责要单一。 一个接口只做一件事。别搞那种“万能接口”,什么都能干,最后谁也接不住。
- 参数类型要通用。 尽量用基础类型(int、string、byte[]),别用平台特有的类型。比如 Android 的
Bitmap,iOS 的UIImage,这些就别出现在接口里了。 - 返回值要稳定。 返回错误码或者自定义异常,别把平台异常直接抛出去。
来看一个我常用的接口设计示例:
// 平台无关的网络请求接口
public interface INetworkService {
// 异步请求,返回结果通过回调
void request(String url,
Map<String, String> headers,
INetworkCallback callback);
// 取消请求
void cancel(String requestId);
}
// 回调接口也是平台无关的
public interface INetworkCallback {
void onSuccess(String response);
void onError(int errorCode, String errorMessage);
}
你看,这里没有出现 NSURLSession,也没有 OkHttp。接口只定义了“做什么”,不关心“怎么做”。
接口隔离原则:别让调用方知道太多
接口隔离原则(ISP)在抽象层设计里特别重要。什么意思呢?就是不要让调用方依赖它不需要的方法。
我曾经见过一个项目,一个 IDeviceInfo 接口里塞了二十多个方法:获取屏幕尺寸、获取电池电量、获取设备型号、获取传感器列表……结果每个平台实现类都得实现所有方法,哪怕有些方法根本用不上。
正确的做法是:把大接口拆成小接口。
| 错误做法 | 正确做法 |
|---|---|
| IDeviceInfo(20+方法) | IScreenInfo、IBatteryInfo、ISensorInfo |
| 调用方被迫依赖不需要的方法 | 调用方只依赖自己需要的接口 |
| 修改一个方法影响所有实现 | 修改只影响相关接口 |
嗯,这里要注意:接口拆得太细也不行,会变成“接口爆炸”。找到一个合适的粒度,需要经验积累。
依赖注入:让抽象层活起来
接口定义好了,谁来提供实现?这就是依赖注入(DI)的用武之地。
依赖注入说白了就是:别让对象自己去找依赖,而是由外部把依赖“注入”进来。
我习惯用构造函数注入,简单直接:
// 业务类只依赖接口,不依赖具体实现
public class FileManager {
private IFileStorage storage;
// 依赖通过构造函数注入
public FileManager(IFileStorage storage) {
this.storage = storage;
}
public void saveUserData(String data) {
storage.write("user_data.txt", data);
}
}
这样写的好处是:测试时可以传入 mock 实现,切换平台时只需要换一个实现类。我在项目中就用这种方式,从 Android 切到 iOS,业务代码一行没改。
一张图看懂抽象层设计
下面这张 SVG 图展示了抽象层的核心结构:
从这张图可以看得很清楚:业务层只跟抽象层打交道,抽象层定义接口,具体实现由各平台完成。这就是“依赖倒置”——高层模块不依赖低层模块,两者都依赖抽象。
避坑指南
- 接口里用了平台特有的数据类型,结果换平台时接口也得改。
- 接口方法参数太多,超过5个参数时调用方很难用。
- 没有考虑异步场景,同步接口在 UI 线程上直接卡死。
我的建议是:接口设计时多想想“如果换一个平台,这个接口还能用吗?”如果答案是否定的,那就得重新设计。
好了,抽象层设计就聊到这里。记住一句话:抽象层是跨平台的基石,设计得好,后面一路顺畅;设计得不好,后面全是坑。
- 抽象层的作用:隔离业务逻辑和平台细节
- 设计平台无关接口:用通用类型、单一职责、稳定返回值
- 接口隔离原则:拆大接口为小接口,避免调用方依赖不需要的方法
- 依赖注入:通过构造函数或 DI 容器注入实现,方便测试和切换平台