天天看点

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 的设计就是一个很好的范例;