应设计终端无关的能力契约接口(如Authenticator、NotificationSender),由各终端实现类按自身约束履行,结合泛型、Javadoc契约注释和结果类型强化LSP合规性,再通过组合扩展接口应对差异化需求。

设计多态接口来规范不同终端(如 Web、App、IoT 设备、小程序等)的行为,关键不是让一个接口“适配所有终端”,而是用接口表达统一能力契约,再由各终端实现类按自身约束忠实履行——这正是面向接口编程与里氏替换原则(LSP)的结合点。
明确终端无关的能力契约
接口不描述“谁来调”或“在哪跑”,而定义“能做什么、结果意味着什么”。例如:
-
不要写
WebLoginService或AppPushNotifier—— 这是实现类命名,不是接口 -
要写
Authenticator:契约是“给定凭证,返回有效用户标识或明确失败原因(如 INVALID_CREDENTIALS、RATE_LIMITED)”,不规定 HTTP 状态码、Token 格式或是否弹窗 -
要写
NotificationSender:契约是“发送一条带标题、内容、优先级的消息,成功即送达终端可感知范围”,不约定是 APNs、FCM 还是 MQTT 主题推送
用方法签名+契约注释锁定行为边界
Java 接口本身不支持运行时校验,所以契约必须靠文档化+类型设计双重约束:
- 每个抽象方法上方用 Javadoc 明确:前置条件(如 “
userId非 null 且长度 ≤ 32”)、后置条件(如 “返回非 null 的不可变UserProfile对象”)、异常语义(如 “凭证过期时抛AuthExpiredException,不抛RuntimeException”) - 用泛型限定缩小误用可能:比如
<T extends TerminalContext> T getContext()比裸Object getContext()更易推理 - 避免
void process(Request req)这类模糊方法;改用Result<ResponseData> handle(Request req),强制实现类表明成功/失败/重试语义
按终端特性分层实现,不强求行为一致
多态不等于“所有终端表现一样”,而是“调用方不用改代码就能换终端”。例如:
立即学习“Java免费学习笔记(深入)”;
-
WebAuthenticator可能依赖 Session + Cookie,做 302 跳转 -
AppAuthenticator使用 JWT + Refresh Token,走 API 返回 JSON -
IoTAuthenticator仅校验设备证书 + 短时效 Token,无 UI 流程 - 三者都实现
Authenticator,只要它们对同一login(credential)输入,都返回符合契约的UserSession或抛指定异常,就满足 LSP
用组合替代“一接口打天下”
当终端差异过大(如有的要离线缓存、有的要实时同步),别硬塞进一个接口。推荐:
- 拆出核心能力接口(如
DataFetcher)和扩展能力接口(如OfflineCapable、RealtimeListener) - 终端实现类按需组合:Web 实现
DataFetcher + RealtimeListener,车载终端实现DataFetcher + OfflineCapable - 调用方通过
instanceof或策略注册表判断是否支持某扩展能力,而非在主接口里加一堆可选方法


















