)
06. 适配器模式Adapter Pattern分类: 结构型模式热门度: ★★★★☆难度: ★★☆☆☆ 概念适配器模式将一个类的接口转换成客户端期望的另一个接口使得原本由于接口不兼容而不能一起工作的类可以一起工作。它就像电源适配器一样把不匹配的插头转换成匹配的接口。适配器有两种实现方式类适配器通过多重继承和对象适配器通过组合更常用。 意图将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作实现接口之间的兼容。 关键角色角色说明目标接口Target客户端期望的接口被适配者Adaptee已存在的、接口不兼容的类适配器Adapter将被适配者的接口转换为目标接口客户端Client通过目标接口使用适配器⚠️ 注意事项对象适配器组合比类适配器继承更灵活推荐使用适配器不应增加过多的转换逻辑否则说明两个接口差异太大适配器模式是事后补救措施如果能在设计阶段统一接口更好可以结合工厂模式来动态选择适配器注意被适配者的异常处理适配器应该转换或包装异常 实现流程客户端 → 通过目标接口调用方法 → 适配器接收调用 → 适配器将调用转发给被适配者的方法 → 被适配者执行实际操作 → 适配器将结果转换为目标接口格式 → 返回结果给客户端 常见使用场景场景1: 第三方API集成Third-party API Integration将第三方支付SDK的接口适配为系统统一的支付接口。usingSystem;// 目标接口 - 系统统一支付接口publicinterfaceIPaymentGateway{PaymentResultCharge(stringorderId,decimalamount,stringcurrency);PaymentResultRefund(stringtransactionId,decimalamount);}publicclassPaymentResult{publicboolSuccess{get;set;}publicstringTransactionId{get;set;}publicstringMessage{get;set;}}// 第三方SDK被适配者- 接口不兼容publicclassStripeSdk{publicStripeResponseCreateCharge(StripeChargeRequestrequest){returnnewStripeResponse{Statussucceeded,Idch_Guid.NewGuid().ToString(N)[..8],Errornull};}publicStripeResponseCreateRefund(stringchargeId,longamountCents){returnnewStripeResponse{Statussucceeded,Idre_Guid.NewGuid().ToString(N)[..8],Errornull};}}publicclassStripeChargeRequest{publiclongAmountCents{get;set;}publicstringCurrency{get;set;}publicstringDescription{get;set;}}publicclassStripeResponse{publicstringStatus{get;set;}publicstringId{get;set;}publicstringError{get;set;}}// 适配器publicclassStripeAdapter:IPaymentGateway{privatereadonlyStripeSdk_stripe;publicStripeAdapter(StripeSdkstripe){_stripestripe;}publicPaymentResultCharge(stringorderId,decimalamount,stringcurrency){// 适配元→分接口格式转换varrequestnewStripeChargeRequest{AmountCents(long)(amount*100),Currencycurrency.ToLower(),Description$Order:{orderId}};varresponse_stripe.CreateCharge(request);returnnewPaymentResult{Successresponse.Statussucceeded,TransactionIdresponse.Id,Messageresponse.Error??Payment successful};}publicPaymentResultRefund(stringtransactionId,decimalamount){varresponse_stripe.CreateRefund(transactionId,(long)(amount*100));returnnewPaymentResult{Successresponse.Statussucceeded,TransactionIdresponse.Id,Messageresponse.Error??Refund successful};}}// 客户端调用publicclassProgram{publicstaticvoidMain(){// 客户端只依赖IPaymentGateway接口IPaymentGatewaygatewaynewStripeAdapter(newStripeSdk());varresultgateway.Charge(ORD-001,99.99m,CNY);Console.WriteLine($支付{(result.Success?成功:失败)}:{result.TransactionId});varrefundgateway.Refund(result.TransactionId,50.00m);Console.WriteLine($退款{(refund.Success?成功:失败)}:{refund.TransactionId});}}场景2: 遗留系统包装Legacy System Wrapper将老旧系统的接口包装为现代接口使新系统可以无缝调用遗留功能。usingSystem;usingSystem.Threading.Tasks;// 现代接口目标publicinterfaceIInventoryService{TaskInventoryResultGetStockAsync(stringsku);TaskInventoryResultUpdateStockAsync(stringsku,intquantity);TaskboolIsAvailableAsync(stringsku);}publicclassInventoryResult{publicboolSuccess{get;set;}publicstringSku{get;set;}publicintQuantity{get;set;}publicstringMessage{get;set;}}// 遗留系统被适配者publicclassLegacyInventorySystem{publicstringQueryInventory(stringproductCode){// 模拟遗留系统返回格式SKU:数量:状态return${productCode}:150:OK;}publicintAdjustInventory(stringproductCode,intdelta){// 模拟遗留系统返回新库存量Console.WriteLine($[Legacy] Adjusting{productCode}by{delta});return150delta;// 返回调整后的数量}}// 适配器publicclassLegacyInventoryAdapter:IInventoryService{privatereadonlyLegacyInventorySystem_legacy;publicLegacyInventoryAdapter(LegacyInventorySystemlegacy){_legacylegacy;}publicasyncTaskInventoryResultGetStockAsync(stringsku){returnawaitTask.Run((){varraw_legacy.QueryInventory(sku);varpartsraw.Split(:);returnnewInventoryResult{Successparts[2]OK,Skuparts[0],Quantityint.Parse(parts[1]),Messageparts[2]};});}publicasyncTaskInventoryResultUpdateStockAsync(stringsku,intquantity){returnawaitTask.Run((){varcurrent_legacy.QueryInventory(sku);varcurrentQtyint.Parse(current.Split(:)[1]);vardeltaquantity-currentQty;varnewQty_legacy.AdjustInventory(sku,delta);returnnewInventoryResult{Successtrue,Skusku,QuantitynewQty,MessageStock updated};});}publicasyncTaskboolIsAvailableAsync(stringsku){varresultawaitGetStockAsync(sku);returnresult.Successresult.Quantity0;}}// 客户端调用publicclassProgram{publicstaticasyncTaskMain(){IInventoryServiceservicenewLegacyInventoryAdapter(newLegacyInventorySystem());varstockawaitservice.GetStockAsync(SKU-12345);Console.WriteLine($库存查询:{stock.Sku}{stock.Quantity}件 ({stock.Message}));varavailableawaitservice.IsAvailableAsync(SKU-12345);Console.WriteLine($是否有货:{available});}}场景3: 数据格式转换XML to JSON Conversion将输出XML格式的遗留数据源适配为返回JSON格式的现代数据接口。usingSystem;usingSystem.Collections.Generic;usingSystem.Xml.Linq;usingSystem.Text.Json;// 目标接口 - JSON风格数据源publicinterfaceIJsonDataSource{ListDictionarystring,objectQuery(stringcollection);Dictionarystring,objectGetById(stringcollection,stringid);}// 被适配者 - XML数据源publicclassXmlDataSource{publicstringGetXmlData(stringtableName){// 模拟XML数据if(tableNameemployees){returnemployees employee idE001 name张三/name department技术部/department salary15000/salary /employee employee idE002 name李四/name department产品部/department salary18000/salary /employee employee idE003 name王五/name department技术部/department salary16500/salary /employee /employees;}returndata/;}}// 适配器publicclassXmlToJsonAdapter:IJsonDataSource{privatereadonlyXmlDataSource_xmlSource;publicXmlToJsonAdapter(XmlDataSourcexmlSource){_xmlSourcexmlSource;}publicListDictionarystring,objectQuery(stringcollection){varxml_xmlSource.GetXmlData(collection);vardocXDocument.Parse(xml);varresultnewListDictionarystring,object();foreach(varelementindoc.Root.Elements()){vardictnewDictionarystring,object{[id]element.Attribute(id)?.Value};foreach(varchildinelement.Elements()){dict[child.Name.LocalName]child.Value;}result.Add(dict);}returnresult;}publicDictionarystring,objectGetById(stringcollection,stringid){varallQuery(collection);returnall.Find(dd[id]?.ToString()id);}}// 客户端调用publicclassProgram{publicstaticvoidMain(){IJsonDataSourcesourcenewXmlToJsonAdapter(newXmlDataSource());varemployeessource.Query(employees);Console.WriteLine( 全部员工JSON格式);Console.WriteLine(JsonSerializer.Serialize(employees,newJsonSerializerOptions{WriteIndentedtrue}));varempsource.GetById(employees,E002);Console.WriteLine($\n 查找 E002 );Console.WriteLine(JsonSerializer.Serialize(emp,newJsonSerializerOptions{WriteIndentedtrue}));}}✅ 优点接口兼容让不兼容的接口可以协同工作复用性好可以复用已有的类无需修改源码符合开闭原则通过新增适配器来支持新接口不修改已有代码客户端透明客户端不需要知道适配器的存在❌ 缺点增加复杂度引入了额外的类和间接层性能开销适配器增加了调用的间接层可能有微小性能损耗调试困难链式适配器调用增加了调试的复杂性 与其他模式对比模式区别桥接模式桥接是事前设计分离抽象与实现适配器是事后补救装饰器模式装饰器增强功能适配器转换接口代理模式代理控制访问适配器转换接口外观模式外观简化子系统接口适配器转换已有接口