【面向對象設計模式】 適配器模式 (二)

.java

做者 :萬境絕塵 ide

轉載請註明出處 : http://blog.csdn.net/shulianghan/article/details/19077139工具

.ui



適配器模式的意圖 : 使用不一樣接口的類所提供的服務爲客戶端提供其所但願的接口;this

-- 問題解決場景 : 在 類A 中實現了接口中的抽象方法, 客戶端B 已經定義好了方法的調用, 可是調用的方法 與 類A 中的方法名不一樣, 這時咱們就須要適配器模式了;spa

-- eg : 類A 實現了接口A1, 類B 實現了接口B1, 這裏C調用 A 和 B 但願 A 和 B 能提供相同方法的接口, 這時咱們須要使用適配器模式;.net



1. 接口適配



(1) 接口適配簡介


接口適配設計

-- 問題場景 : 客戶端須要調用 客戶端類接口 中提供的 requiredMethod()的方法, 可是工具類中只提供了一個 existMethod() 方法, 顯然客戶端接口 與 工具類中提供的方法名稱不匹配;code

-- 適配方案 :  建立一個 適配器類, 適配現有的代碼 工具類,  該類實現客戶端接口的 requiredMethod()抽象方法, 與客戶端接口是實現關係, 同時該實現類繼承 工具類, 能夠調用工具類中的方法, 與工具類的關係是 繼承關係;component

-- 方法委託 : 經過接口適配, 就將 客戶端類的requiredMethod() 方法 委派給了 existMethod()方法;





(2) 接口適配實例


.

接口適配需求

-- 客戶端提供接口 : 須要研發一種M1坦克, 須要實現接口 getCaliber() 獲取火炮口徑, fire() 開火, run()移動 等方法; 

-- 現有接口 : 現有的坦克 有 getGunCaliber() 獲取火炮口徑, GunFire() 火炮開火, Move() 移動 等方法;

-- 適配要求 : 寫一個適配類, 這個類實現 Panzer 接口, 繼承 Tanker 類, 將Panzer接口的動做委託給 Tanker 類;





接口類

package shuliang.han.displaytest;

public interface Panzer {

	public double getCaliber();
	public void fire();
	public void run();
	
}

實體類

package shuliang.han.displaytest;


public class Tanker {

	private double caliber = 125.0;
	
	public double getGunCaliber() {
		return caliber;
	}
	
	public void gunFire() {
		System.out.println("Fire in the hole !!!");
	}
	
	public void move() {
		System.out.println("Move move !!");
	}
	
}


分析

-- 名稱不匹配 : Tanker類中的方法能夠執行 Panzer 接口中須要的動做, 可是它們的方法名稱不匹配;

-- 變量維護 : 若是建立一個 M1A2SEP 類, 須要在類中維護一個 Tank 對象, 在 Panzer 實現類中調用 對應的 Tank 對象方法;


M1A2SEP 類

package shuliang.han.displaytest;

public class M1A2SEP extends Tanker implements Panzer  {

	@Override
	public double getCaliber() {
		return getGunCaliber();
	}

	@Override
	public void fire() {
		gunFire();
	}

	@Override
	public void run() {
		move();
	}

}



接口適配總結

-- 客戶端接口存在 : 若是客戶端接口中定義了客戶端所期待的行爲, 能夠運用適配器模式, 適配器繼承現有類, 並實現客戶端接口;

-- 客戶端接口不存在 : 若是客戶端沒有定義接口, 可使用對象適配器, 對象適配器至關於 子類適配器;



2. 對象適配


(1) 對象適配簡介


類適配 : 上面的接口適配方式就是類適配, 適配器類須要 實現客戶端接口, 繼承 現有實體類;

對象適配 : 對象適配器採用了委派, 並不是是繼承; 建立一個對象適配器, 繼承客戶端類, 在類中維護一個現有類實例對象, 知足客戶端類需求方法; 

-- 須要場景 : 若是適配的客戶端方法沒有被定義在接口中, 就須要對象適配;




對象適配的方法

-- 適配器類繼承客戶端類 : 對象適配的適配器類 繼承客戶端類對象, 適配器類 的 實例 也是 客戶端類的實例, 由於適配器類是客戶端類的子類;  

-- 適配器類使用現有類 : 適配器類中定義一個 現有類對象做爲成員變量, 經過調用 現有類對象中的方法 來實現客戶端類方法的需求;


(2) 對象適配實例


客戶端類 : 如今有客戶端類 Panzer 裝甲車, 提供 獲取火炮口徑方法 getCaliber(), 移動方法 run(), 開火方法 fire(); 

現有類 : 現有類 Tank 坦克, 提供 獲取火炮口徑方法 getGunCaliber(), 移動方法 move(), 開火方法 gunFire();


客戶端類代碼 : 客戶端類代碼中沒有指定建模所需的接口;

package shuliang.han.adapter;

public class Panzer {

	public double getCaliber(){
		return 0;
	}
	
	public void fire(){
		//TODO
	}
	
	public void run(){
		//TODO
	}
}

現有類代碼

package shuliang.han.adapter;

public class Tank {

	private double caliber = 125.0;
	
	public double getGunCaliber(){
		return caliber;
	}
	
	public void gunFire() {
		System.out.println("Fire in the hole !!!");
	}
	
	public void move() {
		System.out.println("Move Move !!!");
	}
}


UML圖



適配器類

package shuliang.han.adapter;

public class M1A2 extends Panzer {
	
	private Tank tank;
	
	public M1A2() {
		tank = new Tank();
	}
	
	@Override
	public double getCaliber() {
		return tank.getGunCaliber();
	}

	@Override
	public void fire() {
		super.fire();
		tank.gunFire();
	}
	
	@Override
	public void run() {
		super.run();
		tank.move();
	}
	
}


(3) 脆弱的對象適配


對象適配比類適配要脆弱

-- 沒有規範接口 : 對象適配的類中沒有規範的接口, 若是客戶端類出現了變化, 運行時可能出現錯誤;

-- 客戶端類不可預知 : 對象適配類 繼承客戶端類, 首先客戶端類須要將方法 和 變量聲明爲 protected, 即便這樣, 這些類的方法也可能不符合子類意圖;



3. Jtable 對數據適配


(1) Jtable 與 TableModel AbstractTableModel模型 


JTable適配數據方法 : JTable類能夠將實現了TableModel抽象類的數據顯示到圖形界面中;

-- 數據不肯定性 : Java中的Swing 提供了JTable控件用以顯示列表, JTable不知道咱們要顯示什麼數據

-- 適配器 : 將數據交給JTable控件並顯示出來, 須要一個適配器, 這些數據要通過一個適配器接口, 這個接口是 TableModel 抽象類;


TableModel子類實現

-- 抽象方法多 : Jtable定義了許多抽象方法, 其子類必須實現全部的抽象方法, 這樣會很麻煩; 

-- TableModel的樁 : JDK中提供了另外一個抽象類 AbstractTableModel 類, AbstractTableModel 繼承了 TableModel 類, 並實現了絕大部分方法, 咱們能夠定義一個類 去 繼承 AbstractTableModel 類, 並實現咱們感興趣的方法, 沒必要實現全部的方法了;

-- 數據封裝 : 建立一個類 繼承 AbstractTableModel 類, 而後呢實現感興趣的接口;



(2) 實例


實現過程 : 使用JTable 繪製坦克相關數據, 須要建立一個TankTableModel類 繼承 AbstractTableModel 類, 而後將 Tank 類封裝在 TankTableModel 中, 當作其成員變量;


使用對象適配的緣由

-- AbstractTableModel 抽象類 : 該抽象類提供了適配器對象須要實現的接口 (抽象方法), 該抽象類又實現了客戶端 JTable類 期待的接口, 適配器對象必須繼承抽象類;

-- 組合第三對象 : 適配器對象還須要重用第三個對象, 重用對象的方法只能是 繼承 和 組合, Java是單繼承機制, 只能使用組合方式, 即將第三個對象當作適配器類的成員變量;


UML圖



Tank代碼

package shuliang.han.jtable;

public class Tank {

	private double caliber;
	private double speed;
	private String name;
	
	public Tank(double caliber, double speed, String name) {
		this.caliber = caliber;
		this.speed = speed;
		this.name = name;
	}

	public double getCaliber() {
		return caliber;
	}

	public double getSpeed() {
		return speed;
	}

	public String getName() {
		return name;
	}
	
	
}



TankTableModel代碼

package shuliang.han.jtable;

import javax.swing.table.AbstractTableModel;

public class TankTableModel extends AbstractTableModel {

	private Tank tanks[];
	private String names[];
	
	public TankTableModel(Tank[] tanks, String[] names) {
		this.tanks = tanks;
		this.names = names;
	}
	
	@Override
	public int getRowCount() {
		return tanks.length;
	}

	@Override
	public int getColumnCount() {
		return names.length;
	}

	@Override
	public String getColumnName(int column) {
		return names[column];
	}
	
	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		switch(columnIndex){
			case 0 :
				return tanks[rowIndex].getName();
			case 1 :
				return new Double(tanks[rowIndex].getCaliber());
			case 2 :
				return new Double(tanks[rowIndex].getSpeed());
			default :
				return null;
		}
	}

}

ShowTankData代碼

package shuliang.han.jtable;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;

public class ShowTanksData {

	public static void main(String[] args) {
		setFrame();
		JTable jTable = new JTable(getTankTableModel());
		jTable.setRowHeight(36);
		JScrollPane pane = new JScrollPane(jTable);
		pane.setPreferredSize(new Dimension(300, 100));
		display(pane, "坦克數據");
	}
	
	private static void setFrame() {
		Font font = new Font("Dialog", Font.PLAIN, 18);
		UIManager.put("Table.font", font);
		UIManager.put("TableHeader.font", font);
	}
	
	private static TankTableModel getTankTableModel() {
		Tank tank1 = new Tank(120.0, 50.0, "99式");
		Tank tank2 = new Tank(150.0, 2.0, "KV");
		return new TankTableModel(new Tank[]{tank1, tank2}, new String[]{"名稱", "火炮口徑 ", "速度"});
	}
	
	private static void display(Component component, String tittle) {
		JFrame frame = new JFrame(tittle);
		frame.getContentPane().add(component);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.pack();
		frame.setVisible(true);
	}

}


效果圖




4. 識別適配器



MouseAdapter 爲 MouseListener 接口提供樁的實現;



在使用MouseAdapter的時候, 就至關於使用了適配器用戶操做鼠標的時候, 將swing組件接收到的鼠標操做適配給相應的動做處理類中, 即將GUI時間適配給應用程序接口, 使用了Swing適配類, 將一個接口方法委派給一個類的方法去執行;



5. 適配器模式總結


適配器總結 : 適配器模式能夠重用一個現有類, 知足客戶端需求, 將客戶端的調用轉化爲現有方法的調用;

-- 類適配器 : 客戶端的需求經過接口表達出來, 能夠建立一個實現了該接口的適配類, 適配類同時還要繼承現有類;

-- 對象適配 : 客戶端沒有指定接口, 建立一個新適配器類, 實現 繼承客戶端類, 在該類中維護一個現有類的實例對象做爲成員變量;


JTable適配器模式 : 經過定義TableModel接口, JTable組件將客戶端須要的表信息存儲到自身中, 經過自定義適配器對象, 將任何數據適配到表中;


JTable不適用類適配緣由

-- 繼承數量限制 : JTable適配器須要繼承 AbstractTableModel類, 這樣就沒法繼承現有類, 由於只能繼承一個類;

-- 須要維護多個對象 : JTable須要大量數據, 通常是從多個對象中採集的;


設計適配器模式 : 當咱們設計軟件的時候, 充分考慮程序的靈活性, JTable 的設計就是一個很好的範例;


.

做者 :萬境絕塵 

轉載請註明出處 : http://blog.csdn.net/shulianghan/article/details/19077139

.

相關文章
相關標籤/搜索