天天看點

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

擴充卡模式的意圖 : 使用不同接口的類所提供的服務為用戶端提供其所希望的接口;

-- 問題解決場景 : 在 類a 中實作了接口中的抽象方法, 用戶端b 已經定義好了方法的調用, 但是調用的方法 與 類a 中的方法名不同, 這時我們就需要擴充卡模式了;

-- eg : 類a 實作了接口a1, 類b 實作了接口b1, 這裡c調用 a 和 b 希望 a 和 b 能提供相同方法的接口, 這時我們需要使用擴充卡模式;

接口适配 : 

-- 問題場景 : 用戶端需要調用 用戶端類接口 中提供的 requiredmethod()的方法, 但是工具類中隻提供了一個 existmethod() 方法, 顯然用戶端接口 與 工具類中提供的方法名稱不比對;

-- 适配方案 :  建立一個 擴充卡類, 适配現有的代碼 工具類,  該類實作用戶端接口的 requiredmethod()抽象方法,

與用戶端接口是實作關系, 同時該實作類繼承 工具類, 可以調用工具類中的方法, 與工具類的關系是 繼承關系;

-- 方法委托 : 通過接口适配, 就将 用戶端類的requiredmethod() 方法 委派給了 existmethod()方法;

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

.

接口适配需求 : 

-- 用戶端提供接口 : 需要研發一種m1坦克, 需要實作接口 getcaliber() 擷取火炮口徑, fire() 開火, run()移動 等方法; 

-- 現有接口 : 現有的坦克 有 getguncaliber() 擷取火炮口徑, gunfire() 火炮開火, move() 移動 等方法;

-- 适配要求 : 寫一個适配類, 這個類實作 panzer 接口, 繼承 tanker 類,

将panzer接口的動作委托給 tanker 類;

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

接口類 : 

[java] view

plaincopy

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)
Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

package shuliang.han.displaytest;  

public interface panzer {  

    public double getcaliber();  

    public void fire();  

    public void run();  

}  

實體類 : 

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)
Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

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 類 : 

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)
Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

public class m1a2sep extends tanker implements panzer  {  

    @override  

    public double getcaliber() {  

        return getguncaliber();  

    public void fire() {  

        gunfire();  

    public void run() {  

        move();  

接口适配總結 : 

-- 用戶端接口存在 : 如果用戶端接口中定義了用戶端所期待的行為, 可以運用擴充卡模式, 擴充卡繼承現有類, 并實作用戶端接口;

-- 用戶端接口不存在 : 如果用戶端沒有定義接口, 可以使用對象擴充卡, 對象擴充卡相當于 子類擴充卡;

類适配 : 上面的接口适配方式就是類适配, 擴充卡類需要 實作用戶端接口, 繼承 現有實體類;

對象适配 : 對象擴充卡采用了委派, 并非是繼承; 建立一個對象擴充卡, 繼承用戶端類,

在類中維護一個現有類執行個體對象, 滿足用戶端類需求方法; 

-- 需要場景 : 如果适配的用戶端方法沒有被定義在接口中, 就需要對象适配;

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

對象适配的方法 : 

-- 擴充卡類繼承用戶端類 : 對象适配的擴充卡類 繼承用戶端類對象, 擴充卡類 的 執行個體 也是 用戶端類的執行個體, 因為擴充卡類是用戶端類的子類;  

-- 擴充卡類使用現有類 : 擴充卡類中定義一個 現有類對象作為成員變量, 通過調用 現有類對象中的方法 來實作用戶端類方法的需求;

用戶端類 : 現在有用戶端類 panzer 裝甲車, 提供 擷取火炮口徑方法 getcaliber(), 移動方法 run(), 開火方法 fire(); 

現有類 : 現有類 tank 坦克, 提供 擷取火炮口徑方法 getguncaliber(), 移動方法 move(), 開火方法 gunfire();

用戶端類代碼 : 用戶端類代碼中沒有指定模組化所需的接口;

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)
Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

package shuliang.han.adapter;  

public class panzer {  

    public double getcaliber(){  

        return 0;  

    public void fire(){  

        //todo  

    public void run(){  

現有類代碼 : 

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)
Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

public class tank {  

    public double getguncaliber(){  

        system.out.println("move move !!!");  

uml圖 : 

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

擴充卡類 : 

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)
Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

public class m1a2 extends panzer {  

    private tank tank;  

    public m1a2() {  

        tank = new tank();  

        return tank.getguncaliber();  

        super.fire();  

        tank.gunfire();  

        super.run();  

        tank.move();  

對象适配比類适配要脆弱 : 

-- 沒有規範接口 : 對象适配的類中沒有規範的接口, 如果用戶端類出現了變化,

運作時可能出現錯誤;

-- 用戶端類不可預知 : 對象适配類 繼承用戶端類, 首先用戶端類需要将方法 和 變量聲明為 protected, 即使這樣, 這些類的方法也可能不符合子類意圖;

jtable适配資料方法 : jtable類可以将實作了tablemodel抽象類的資料顯示到圖形界面中;

-- 資料不确定性 : java中的swing 提供了jtable控件用以顯示清單, jtable不知道我們要顯示什麼資料; 

-- 擴充卡 : 将資料交給jtable控件并顯示出來, 需要一個擴充卡, 這些資料要經過一個擴充卡接口, 這個接口是 tablemodel 抽象類;

tablemodel子類實作 : 

-- 抽象方法多 : jtable定義了許多抽象方法, 其子類必須實作所有的抽象方法, 這樣會很麻煩; 

-- tablemodel的樁 : jdk中提供了另一個抽象類 abstracttablemodel 類, abstracttablemodel 繼承了 tablemodel 類, 并實作了絕大部分方法,

我們可以定義一個類 去 繼承 abstracttablemodel 類, 并實作我們感興趣的方法, 不必實作所有的方法了;

-- 資料封裝 : 建立一個類 繼承 abstracttablemodel 類, 然後呢實作感興趣的接口;

實作過程 : 使用jtable 繪制坦克相關資料, 需要建立一個tanktablemodel類 繼承 abstracttablemodel 類, 然後将 tank 類封裝在 tanktablemodel

中, 當做其成員變量;

使用對象适配的原因 : 

-- abstracttablemodel 抽象類 : 該抽象類提供了擴充卡對象需要實作的接口 (抽象方法), 該抽象類又實作了用戶端 jtable類 期待的接口,

擴充卡對象必須繼承抽象類;

-- 組合第三對象 : 擴充卡對象還需要重用第三個對象, 重用對象的方法隻能是

繼承 和 組合, java是單繼承機制, 隻能使用組合方式, 即将第三個對象當做擴充卡類的成員變量;

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

tank代碼 : 

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)
Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

package shuliang.han.jtable;  

    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 getspeed() {  

        return speed;  

    public string getname() {  

        return name;  

tanktablemodel代碼 : 

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)
Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

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;  

    public int getrowcount() {  

        return tanks.length;  

    public int getcolumncount() {  

        return names.length;  

    public string getcolumnname(int column) {  

        return names[column];  

    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代碼 : 

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)
Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

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);  

效果圖 : 

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

mouseadapter 為 mouselistener 接口提供樁的實作;

Java 設計模式 接口型模式 之 擴充卡Adapter模式 (二)

在使用mouseadapter的時候, 就相當于使用了擴充卡 : 使用者操作滑鼠的時候, 将swing元件接收到的滑鼠操作适配給相應的動作處理類中,

即将gui時間适配給應用程式接口, 使用了swing适配類, 将一個接口方法委派給一個類的方法去執行;

擴充卡總結 : 擴充卡模式可以重用一個現有類, 滿足用戶端需求, 将用戶端的調用轉化為現有方法的調用;

-- 類擴充卡 : 用戶端的需求通過接口表達出來, 可以建立一個實作了該接口的适配類, 适配類同時還要繼承現有類;

-- 對象适配 : 用戶端沒有指定接口, 建立一個新擴充卡類, 實作 繼承用戶端類, 在該類中維護一個現有類的執行個體對象作為成員變量;

jtable擴充卡模式 : 通過定義tablemodel接口, jtable元件将用戶端需要的表資訊存儲到自身中, 通過自定義擴充卡對象, 将任何資料适配到表中;

jtable不适用類适配原因 : 

-- 繼承數量限制 : jtable擴充卡需要繼承 abstracttablemodel類, 這樣就無法繼承現有類, 因為隻能繼承一個類;

-- 需要維護多個對象 : jtable需要大量資料, 一般是從多個對象中采集的;

設計擴充卡模式 : 當我們設計軟體的時候, 充分考慮程式的靈活性, jtable 的設計就是一個很好的範例;