一句话解释:
两个无关联的类,通过实现同一接口或继承对方得到新的适配器类,新的适配器类中通过实现原本类的操作,可达到进行相同的操作的目的。
适配器模式(Apapter Pattern)是一种结构型设计模式,用于将一个类的实现转换成客户端所期望的另一个类,这个类中的操作和目标类具有相同的操作,以达到两个不相关的类可以互操作的目的。
官方意图:将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
一个比喻:(两位来自不同方言区的学生)
分别来自广州和海口的两位同学,如果都各自说自己的家乡话粤语和海南话,则根本无法沟通,此时普通话就是一个适配器,将两种方言翻译成普通话来进行适配,这样就可以顺畅沟通了。
优缺点:
适用场景:
实际使用场景举例:
下面是一个使用适配器设计模式的示例,假设我们有一个电源插座接口 ISocket,其中定义了供电方法 SupplyPower():
class Program // 测试
{
static void Main(string[] args)
{
// 国标插座
ChinaSocket chinaSocket = new ChinaSocket();
ChinaSocketAdapter chinaAdapter = new ChinaSocketAdapter(chinaSocket);
Laptop laptop1 = new Laptop(chinaAdapter); // 笔记本1 通过国标版插座充电
laptop1.Charge();
// 美版插座
USASocket usaSocket = new USASocket();
USASocketAdapter usaAdapter = new USASocketAdapter(usaSocket);
Laptop laptop2 = new Laptop(usaAdapter); // 同一类型的笔记本2 通过美标版插座充电
laptop2.Charge();
Console.ReadKey();
}
}
// 电源插座接口
public interface ISocket
{
void SupplyPower();
}
// 国标版插口
public class ChinaSocket
{
public string name { get; set; }
public void Charge()
{
Console.WriteLine("开始充电!");
}
}
// 美版插口
public class USASocket
{
public string name { get; set; }
public void PowerUp()
{
Console.WriteLine("Start charging!");
}
}
// 国标版的适配器,实现统一的电源插座接口
public class ChinaSocketAdapter : ISocket
{
private readonly ChinaSocket _chinaSocket;
public ChinaSocketAdapter(ChinaSocket chinaSocket)
{
_chinaSocket = chinaSocket;
}
public void SupplyPower() // 国标版实现充电方法,调用特有的 Charge() 方法
{
_chinaSocket.Charge();
}
}
// 美版插座适配器,实现统一的插座接口
public class USASocketAdapter : ISocket
{
private readonly USASocket _usaSocket;
public USASocketAdapter(USASocket usaSocket)
{
_usaSocket = usaSocket;
}
public void SupplyPower() // 美版实现充电方法,调用特有的 PowerUp() 方法
{
_usaSocket.PowerUp();
}
}
// 笔记本电脑类实现,统一充电方法 Charge()
public class Laptop
{
private readonly ISocket _socket;
public Laptop(ISocket socket)
{
_socket = socket;
}
public void Charge()
{
_socket.SupplyPower();
}
}
最终,无论是国标还是美版的插座,均调用 Charge() 方法进行充电,充当适配器角色的就是电源插座接口 IScoket。
如下简单画一下上一章节示例代码对应的类图:(美版相似就省略了,只显示国标版)
Client:给 Laptop 声明插座并完成充电动作。
Laptop:定义统一充电接口的模板,供适配器实现。
ChinaSocketAdapter:对 ChinaSocket 类和 Laptop 进行适配。
ChinaSocket:已存在的类,此类需要适配。
IDataAdapter 接口的实现类通常是数据库访问器,它们提供了一种与特定数据库类型无关的方式来访问数据。
下面是接口 IDataAdapter 的源码:
// System.Data.Common, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Data.IDataAdapter
using System.Data;
using System.Diagnostics.CodeAnalysis;
public interface IDataAdapter
{
MissingMappingAction MissingMappingAction { get; set; }
MissingSchemaAction MissingSchemaAction { get; set; }
ITableMappingCollection TableMappings { get; }
[RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")]
DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType);
int Fill(DataSet dataSet);
IDataParameter[] GetFillParameters();
[RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")]
int Update(DataSet dataSet);
}
具体的数据库,均实现了 IDataAdapter 接口:
// SQL Server
public sealed class SqlDataAdapter : DbDataAdapter, IDataAdapter, IDbDataAdapter, ICloneable
{ ... }
// Oracle
[Designer("Oracle.VsDevTools.OracleVSGDataAdapterWizard, Oracle.VsDevTools, Version=4.122.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342, processorArchitecture=MSIL", typeof(IDesigner))]
[ToolboxBitmap(typeof(resfinder), "Oracle.ManagedDataAccess.src.Client.Icons.OracleDataAdapterToolBox_hc.bmp")]
[DefaultEvent("RowUpdated")]
public sealed class OracleDataAdapter : DbDataAdapter, IDbDataAdapter, IDataAdapter
{ ... }
// MySQL
[DesignerCategory("Code")]
[Designer("MySql.Data.MySqlClient.Design.MySqlDataAdapterDesigner,MySqlClient.Design")]
public sealed class MySqlDataAdapter : DbDataAdapter, IDbDataAdapter, IDataAdapter
{ ... }
通过使用 IDataAdapter 接口,我们可以编写与特定数据库类型无关的代码,而只需要关心数据集的结构和操作。这样,我们就可以更轻松地更换或扩展我们的数据源,而不需要修改应用程序的代码。同时,IDataAdapter 接口还支持数据映射和数据验证等高级功能,使得我们可以更好地处理复杂的数据操作。
桥接模式(Bridge)的结构与对象适配器类似,但是 Bridge 模式的出发点不同。Bridge 的目的是将接口部分和实现部分分离,从而可以对它们较为容易也相对独立地加以改变。而 Adapter 则意味着改变一个已有对象的接口。
装饰模式(Decorator)增强了其他对象的功能而同时又不改变它的接口,因此 Decorator 对应用程序的透明性比适配器要好。结果是 Decorator 支持递归组合,而纯粹使用适配器是不可能实现这一点的。
代理模式(Proxy)在不改变它的接口的条件下,为另一个对象定义了一个代理。