适配器模式
定义
将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
解决
主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
优点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
- 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
- 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”
类适配器模式还具有如下优点:
由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器模式还具有如下优点:
一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
缺点
类适配器模式的缺点如下:
对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。
对象适配器模式的缺点如下:
与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。
结构
适配器模式包含如下角色:
- Target:目标抽象类
- Adapter:适配器类
- Adaptee:适配者类
- Client:客户类
实现
package adaptermethod;
/**
* 用户期待的接口,可以是接口,可以是抽象类
*/
public abstract class Target {
public void request() {
System.out.println("普通请求!");
}
}
package adaptermethod;
/**
* 适配器类,通过在内部包装一个Adaptee对象,把原接口转换成目标接口
*/
public class Adapter extends Target{
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
adaptee.specificRequest();
}
}
package adaptermethod;
/**
* 需要适配的类
*/
public class Adaptee {
public void specificRequest() {
System.out.println("特殊的请求!");
}
}
package adaptermethod;
public class AdapterClient {
public static void main(String[] args) {
Target target;
target = new Adapter();
//客户端只管调用target的request()
//然后实际是用Adptee实现的方法
target.request();
}
}
实例
火箭队(目标target)打球,姚明作为外籍球员(适配者)打球,刚进队听不懂,需要翻译(即适配器)
package adaptermethod.demo;
/**
* 球员 (Target)
*/
public abstract class Player {
protected String name;
public Player(String name)
{
this.name=name;
}
public abstract void attack();
public abstract void defence();
}
//前锋
class Forwards extends Player{
public Forwards(String name) {
super(name);
}
@Override
public void attack() {
System.out.println("前锋 "+name+" 攻击");
}
@Override
public void defence() {
System.out.println("前锋 "+name+" 攻击");
}
}
//中锋
class Center extends Player{
public Center(String name) {
super(name);
}
@Override
public void attack() {
System.out.println("中锋 "+name+" 攻击");
}
@Override
public void defence() {
System.out.println("中锋 "+name+" 攻击");
}
}
//后卫
class Guards extends Player{
public Guards(String name) {
super(name);
}
@Override
public void attack() {
System.out.println("后卫 "+name+" 攻击");
}
@Override
public void defence() {
System.out.println("后卫 "+name+" 防守");
}
}
package adaptermethod.demo;
/**
* 适配器 (Adapter)
*/
public class Translator extends Player{
private ForeignPlayer foreignPlayer=new ForeignCenter();
//给中锋的翻译
public Translator(String name) {
super(name);
foreignPlayer.name=name;
}
@Override
public void attack() {
foreignPlayer.jingong();
}
@Override
public void defence() {
foreignPlayer.fangshou();
}
}
package adaptermethod.demo;
//外籍球员 Adaptee
public abstract class ForeignPlayer {
//外籍球abstract class ForeignPlayer {
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void jingong();
public abstract void fangshou();
}
//外籍中峰
class ForeignCenter extends ForeignPlayer{
@Override
public void jingong() {
System.out.println("中锋 "+name+" 攻击");
}
@Override
public void fangshou() {
System.out.println("中锋 "+name+" 防守");
}
}
package adaptermethod.demo;
/**
* 火箭队(目标target)打球,姚明作为外籍球员(适配者)打球,刚进队听不懂,需要翻译(即适配器)
*/
public class AdapterClient {
public static void main(String[] args) {
Player b=new Forwards("巴蒂尔");
b.attack();
Player m=new Guards("麦克格雷迪");
m.attack();
Player ym =new Translator("姚明");
ym.attack();
ym.defence();
}
}
总结
- 适配器模式用于将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
- 适配器模式包含四个角色:目标抽象类定义客户要用的特定领域的接口;适配器类可以调用另一个接口,作为一个转换器,对适配者和抽象目标类进行适配,它是适配器模式的核心;适配者类是被适配的角色,它定义了一个已经存在的接口,这个接口需要适配;在客户类中针对目标抽象类进行编程,调用在目标抽象类中定义的业务方法。
- 在类适配器模式中,适配器类实现了目标抽象类接口并继承了适配者类,并在目标抽象类的实现方法中调用所继承的适配者类的方法;在对象适配器模式中,适配器类继承了目标抽象类并定义了一个适配者类的对象实例,在所继承的目标抽象类方法中调用适配者类的相应业务方法。
- 适配器模式的主要优点是将目标类和适配者类解耦,增加了类的透明性和复用性,同时系统的灵活性和扩展性都非常好,更换适配器或者增加新的适配器都非常方便,符合“开闭原则”;类适配器模式的缺点是适配器类在很多编程语言中不能同时适配多个适配者类,对象适配器模式的缺点是很难置换适配者类的方法。
- 适配器模式适用情况包括:系统需要使用现有的类,而这些类的接口不符合系统的需要;想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类一起工作。