Adapter需实现Target接口并组合Adaptee实例,通过构造函数注入Adaptee,Request()内调用SpecificRequest()完成翻译;禁用多继承和暴露Adaptee方法,优先选对象适配器。

Adapter 类怎么写才能桥接两个不兼容接口
核心是让 Target 接口的调用方完全 unaware(无感知)背后实际走的是 Adaptee 的逻辑。Adapter 必须继承或实现 Target,同时持有 Adaptee 实例——不是“继承 Adaptee”,而是“组合它”。常见错误是让 Adapter 同时继承 Adaptee(C# 不支持多继承)或暴露 Adaptee 的方法到公共接口,这会破坏封装和依赖倒置。
实操建议:
- 定义清晰的
ITarget接口(比如void Request()),这是客户端唯一依赖的契约 - 定义已有但不匹配的
Adaptee类(比如它只有void SpecificRequest()) - Adapter 构造函数接收
Adaptee实例并保存为私有字段,Request()内部调用adaptee.SpecificRequest() - 避免在 Adapter 中添加新行为——它只做“翻译”,不做增强;要扩展请用装饰器
对象适配器 vs 类适配器:C# 里只能选前者
C# 不支持多重继承,所以传统 UML 中的“类适配器”(Adapter 继承 Adaptee 并实现 ITarget)无法直接实现。强行用抽象基类模拟会导致耦合加剧、测试困难,且违背“优先组合而非继承”的原则。实际项目中一律采用对象适配器模式。
关键差异点:
- 对象适配器:Adapter 持有
Adaptee引用,可运行时替换不同实例,支持依赖注入 - 类适配器:C# 编译报错
CS0262: Partial declarations have conflicting accessibility modifiers(若尝试多继承)或根本无法声明 - 如果
Adaptee是 sealed 类,对象适配器仍是唯一可行路径
什么时候该用 Adapter 而不是重新封装或改接口
Adapter 不是万能胶水,它适用场景很明确:你无法修改 Adaptee(第三方库、遗留系统、SDK)、也不能要求调用方改用新接口,但又必须让两者对接。一旦出现以下任一情况,就该停手换方案:
-
Adaptee方法签名频繁变动 → Adapter 会变成维护黑洞,不如用工厂+策略封装变化点 - 需要把多个
Adaptee映射到同一个ITarget→ 这其实是 Facade 或 Composite 的职责 - Adapter 内部开始做数据转换、重试、日志等非适配逻辑 → 职责污染,应拆出独立服务
- 发现 80% 的
ITarget方法只是透传,另 20% 需复杂映射 → 可能说明接口设计本身失衡,先审视契约合理性
泛型 Adapter 如何避免类型爆炸
当面对多个相似但参数不同的 Adaptee(如 LoggerV1 / LoggerV2),别为每个版本写一个 Adapter 类。用泛型约束 + 委托注入更轻量:
public class GenericAdapter<TAdaptee> : ITarget
where TAdaptee : class
{
private readonly TAdaptee _adaptee;
private readonly Action<TAdaptee> _adaptAction;
<pre class="brush:php;toolbar:false;">public GenericAdapter(TAdaptee adaptee, Action<TAdaptee> adaptAction)
{
_adaptee = adaptee;
_adaptAction = adaptAction;
}
public void Request() => _adaptAction(_adaptee);}
这样复用性高,但要注意:泛型 Adapter 容易掩盖真实依赖,单元测试时需确保 _adaptAction 行为确定;生产环境慎用于跨层适配(如 UI 层适配数据访问层),会模糊分层边界。
真正难的不是写一个 Adapter,而是判断该不该引入它——多数时候,接口不兼容的根源在于前期契约没对齐,补 Adapter 只是推迟了问题爆发时间。










