一、定义
GOF 中对Adapter的描述是这样的:将一个类的接口换成客户希望的另一个接口。Adapter模式使得原来由于接口不兼容而不能在一起工作的那些类可以一起工作。别名又叫包装器(Wrapper)。
二、场景设计
适配器思想在生活中随处可见。举个例子,给手机充电,电脑在跟前的时候,我习惯直接用数据线连接手机和电脑的USB接口;若周围没有可供连接的USB接口,只有普通电源插座时,数据线就不能直接拿来充电了。这时候,我们需要一个电源适配器,这个适配器有和普通电源插座匹配的插头,同时USB数据线也可以连接手机和适配器。这样组合起来,就可以在目标接口(普通电源插座)和现有类的接口(USB)不兼容的情况下,做到利用现有的类(数据线)来完成任务了(充电)!
三、适用性
以下情况使用Adapter模式:
(1)想使用已存在的一个类,而他的接口不符合你的需求
(2)想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作
(3)(仅适用对象Adapter)想使用一些已经存在的子类,但是不可能对每个都进行子类化以匹配他们的接口。对象适配器可以适配他们的父类接口。
四、类图
类适配器
对象适配器
类适配器和对象适配器各有权衡,类适配器:
(1)当我们想匹配一个类以及它所有的子类是,类Adapter将无法胜任。
(2)Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的子类。
对象适配器则:
(1)一个Adapter可以与多个Adaptee——即Adaptee本身以及它的所有子类同时工作。
(2)重定义Adaptee的行为比较困难,你要重定义一个Adaptee的子类。
Anyway ,在java中,如果Target是个抽象类的话,那必须使用对象适配器,因为java不支持类的多继承。
五,应用例子
javax.swing包中的JTable类就是应用对象适配器的典型例子。在构建JTable类的对象时,它要求我们写一个对象适配器,该类根据对象适配器提供的信息来创建一个用户图形界面的表组件。javax.swing.table包中定义了一个AbstractTableModel抽象类,来限定适配器要实现的接口。
Rocket.java
public class Rocket {
String name;
double price;
long apogee;
Rocket(String name,double price,long apogee)
{
this.name=name;
this.price=price;
this.apogee=apogee;
}
public String getName()
{
return this.name;
}
public double getPrice()
{
return this.price;
}
public long getApogee()
{
return this.apogee;
}
public void setName(String name)
{
this.name=name;
}
public void setPrice(double price)
{
this.price=price;
}
public void setApogee(long apogee)
{
this.apogee=apogee;
}
}
Client.java
import java.awt.Component;
import java.awt.Font;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
class RocketTable extends AbstractTableModel
{
protected Rocket[] rockets;
protected String[] columnNames=new String[]{"Name","Price","Apogee"};
public RocketTable(Rocket[] rockets)
{
this.rockets=rockets;
}
@Override
public int getRowCount() {
// TODO Auto-generated method stub
return rockets.length;
}
@Override
public int getColumnCount() {
// TODO Auto-generated method stub
return columnNames.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
// TODO Auto-generated method stub
Rocket temp=rockets[rowIndex];
if(columnIndex==0)
{
return temp.getName();
}
if(columnIndex==1)
{
return temp.getPrice();
}
if(columnIndex==2)
{
return temp.getApogee();
}
return null;
}
}
public class Client {
private static void setFonts()
{
Font f=new Font("Dialog",Font.PLAIN,18);
UIManager.put("Table.font", f);
UIManager.put("TableHeader.font", f);
}
private static RocketTable getRocketTable()
{
Rocket r1=new Rocket("Shooter",3.95,50);
Rocket r2=new Rocket("Orbit",29.95,5000);
Rocket r3=new Rocket("Sun",40,7000);
return new RocketTable(new Rocket[]{r1,r2,r3});
}
public static void display(Component c,String title)
{
JFrame f=new JFrame(title);
f.getContentPane().add(c);
f.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
f.pack();
f.setVisible(true);
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
setFonts();
JTable t=new JTable(getRocketTable());
t.setRowHeight(36);
JScrollPane jsp=new JScrollPane(t);
jsp.setPreferredSize(new java.awt.Dimension(300,100));
display(jsp,"Rockets");
}
}
这里,调用方(JTable)通过java接口(AbstractTableModel)限定了适配器类(RocketTable)应具有的功能。在适配器中,由Adaptee(Rocket对象)来提供所需的数据。