Adapter—适配器模式

本文深入探讨了Adapter设计模式的定义、应用场景、适用性、类图和具体应用实例,如JTable类如何通过对象适配器实现与现有类的接口兼容。详细解释了类适配器与对象适配器的区别,并以Rocket类和Client类为例,展示了如何通过适配器模式实现实现复杂接口转换的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、定义

        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对象)来提供所需的数据。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值