简介
适配器模式(Adapter Pattern):就是将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
在计算机编程中,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
适配器有类适配器和对象适配器两种类型,二者的意图相同,只是实现的方法和适用的情况不同。类适配器采用继承来实现,对象适配器则采用组合的方法来实现。
角色
- 目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
- 需要适配的类(Adaptee):需要适配的类或适配者类。
- 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
优点
- 通过适配器,客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单、更直接、更紧凑。
- 复用了现存的类,解决了现存类和复用环境要求不一致的问题。
- 将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
- 一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
缺点
- 对于对象适配器来说,更换适配器的实现过程比较复杂。(可以用工厂模式解决)
应用场景
- 系统需要使用现有的类,而此类的接口不符合系统的需要。
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。
- 仅对对象适配器而言,在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。
案例
我们开发了一个项目,项目中采用了一个第三方的日志组件来记录日志,但是现在第三方日志组件实现的,现在我们自己开发了一个日志组件,我们想把项目中的日志组件替换为我们自己开发的,但是我们自己的开发的日志组件并没有实现第三方组件的接口,这个时候要怎么处理?
对象适配器实现(推荐)
- 目标接口
/// <summary>
/// 目标接口
/// </summary>
public interface ILog
{/// <summary>/// 写日志的方法/// </summary>public void Write(string logCommand);
}
- 需要适配的类
/// <summary>
/// 新开发的的日志接口类
/// </summary>
public abstract class ILogAbapter
{/// <summary>/// 新的写日志的方法/// </summary>/// <param name="logCommand"></param>public abstract void NewWrite(string logCommand);
}/// <summary>
/// 需要适配的数据库日志类
/// </summary>
public class DbLog : ILogAbapter
{public override void NewWrite(string logCommand){Console.WriteLine($"记录到数据库中:{logCommand}");}
}/// <summary>
/// 需要适配的文件日志类
/// </summary>
public class FileLog : ILogAbapter
{public override void NewWrite(string logCommand){Console.WriteLine($"记录到日志文件中:{logCommand}");}
}
- 适配器
/// <summary>
/// 对象适配器
/// </summary>
public class LogAdapter : ILog
{private readonly ILogAbapter _logAdapter = null;public LogAdapter(ILogAbapter logAdapter){_logAdapter = logAdapter;}public void Write(string logCommand){_logAdapter.NewWrite(logCommand);}
}
- 上层应用调用
{#region 对象适配器LogAdapter fileLog = new LogAdapter(new FileLogAdapter());fileLog.Write("hello word");LogAdapter dbLog = new LogAdapter(new DbLogAdapter());dbLog.Write("hello word");#endregion
}
类适配器实现
- 目标接口
/// <summary>
/// 目标接口
/// </summary>
public interface ILog
{/// <summary>/// 写日志的方法/// </summary>public void Write(string logCommand);
}
- 需要适配的类
/// <summary>
/// 新开发的的日志接口类
/// </summary>
public abstract class ILogAbapter
{/// <summary>/// 新的写日志的方法/// </summary>/// <param name="logCommand"></param>public abstract void NewWrite(string logCommand);
}/// <summary>
/// 需要适配的数据库日志类
/// </summary>
public class DbLog : ILogAbapter
{public override void NewWrite(string logCommand){Console.WriteLine($"记录到数据库中:{logCommand}");}
}/// <summary>
/// 需要适配的文件日志类
/// </summary>
public class FileLog : ILogAbapter
{public override void NewWrite(string logCommand){Console.WriteLine($"记录到日志文件中:{logCommand}");}
}
- 适配器
/// <summary>
/// 数据库日志适配器
/// </summary>
public class DbLogAdapter : DbLog, ILog
{public void Write(string logCommand){NewWrite(logCommand);}
}/// <summary>/// 文件日志适配器/// </summary>public class FileLogAdapter : FileLog, ILog{public void Write(string logCommand){NewWrite(logCommand);}}
- 上层应用调用
{#region 类适配器ILog fileLog = new FileLogAdapter();fileLog.Write("hello word");ILog dbLog = new DbLogAdapter();dbLog.Write("hello word");#endregion
}
总结
- 对象适配用处是,可以使Adaptee不必实现不需要的方法,具体就是其表现形式就是父类实现缺省方法,而子类只需实现自己独特的方法。
- 适配器类可以是抽象类。
- 带参数的适配器模式。使用这种办法,适配器类可以根据参数返还一个合适的实例给客户端。