大话设计模式笔记(十四)の适配器模式

举个栗子

问题描述

不同国家的人在NBA打球,但都是用英文交流。

简单实现

Player

/**
 * 球员
 * Created by callmeDevil on 2019/8/4.
 */
public abstract class Player {

    protected String name;

    public Player(String name){
        this.name = name;
    }

    // 发起进攻
    public abstract void attack();
    // 转回防守
    public abstract void defense();

}

Forwards

/**
 * 前锋
 * Created by callmeDevil on 2019/8/4.
 */
public class Forwards extends Player {

    public Forwards(String name){
        super(name);
    }

    @Override
    public void attack() {
        System.out.println(String.format("前锋 %s 进攻", name));
    }

    @Override
    public void defense() {
        System.out.println(String.format("前锋 %s 防守", name));
    }
}

Center

/**
 * 中锋
 * Created by callmeDevil on 2019/8/4.
 */
public class Center extends Player {
    // 代码与前锋相似
}

Guards

/**
 * 后卫
 * Created by callmeDevil on 2019/8/4.
 */
public class Guards extends Player {
    // 代码与前锋相似
}

测试

public class Test {
    public static void main(String[] args) {
        Player b = new Forwards("巴蒂尔");
        b.attack();
        Player m = new Guards("麦克格雷迪");
        m.attack();

        Player ym = new Center("姚明");
        // 姚明问:attack 和 defense 是什么意思?
        ym.attack();
        ym.defense();
    }
}

测试结果

前锋 巴蒂尔 进攻
后卫 麦克格雷迪 进攻
中锋 姚明 进攻
中锋 姚明 防守

存在问题

姚明刚到NBA时可能英文还不太好,也就是说听不懂教练的战术安排,attach 和 defense 不知道什么意思,因此这样实现会有问题,需要一个中英翻译。

适配器模式

定义

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

分类

主要分为类适配器模式对象适配器模式。由于类适配器模式通过多重继承对一个接口与另一个接口进行匹配,而 Java 等语言不支持多重继承,也就是一个类只有一个父类,所以这里主要讲的是对象适配器

UML图

代码实现

ForeignCenter

/**
 * 外籍中锋
 * Created by callmeDevil on 2019/8/4.
 */
public class ForeignCenter {

    // 外籍中锋球员的姓名故意用属性而不是构造方法来区别与三个球员的不同
    private String name;

    // 表明外籍中锋只懂中文“进攻”(注意:举例效果,实际上千万别用这种方式编程)
    public void 进攻(){
        System.out.println(String.format("外籍中锋 %s 进攻", name));
    }

    // 表明外籍中锋只懂中文“防守”(注意:举例效果,实际上千万别用这种方式编程)
    public void 防守(){
        System.out.println(String.format("外籍中锋 %s 防守", name));
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Translator

/**
 * 翻译者
 * Created by callmeDevil on 2019/8/4.
 */
public class Translator extends Player{

    // 声明并实例化一个内部“外籍中锋”对象,表明翻译者与外籍球员有关联
    private ForeignCenter wjzf = new ForeignCenter();

    public Translator(String name){
        super(name);
        wjzf.setName(name);
    }

    @Override
    public void attack() {
        // 翻译者将“attack”翻译为“进攻”告诉外籍中锋
        wjzf.进攻();
    }

    @Override
    public void defense() {
        // 翻译者将“defense”翻译为“防守”告诉外籍中锋
        wjzf.防守();
    }

}

测试

public class Test {
    public static void main(String[] args) {
        Player b = new Forwards("巴蒂尔");
        b.attack();
        Player m = new Guards("麦克格雷迪");
        m.attack();

        Player ym = new Translator("姚明");
        // 翻译者告诉姚明,教练要求你既要“进攻”又要“防守”
        ym.attack();
        ym.defense();
    }
}

测试结果

前锋 巴蒂尔 进攻
后卫 麦克格雷迪 进攻
外籍中锋 姚明 进攻
外籍中锋 姚明 防守

总结

  • 系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。
  • 适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。
  • 使用已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时,就应该考虑用适配器模式。
  • 两个类所做的事情相同或相似,但是具有不同的接口时要使用它。
  • 客户代码可以统一调用同一接口就行了,这样可以更简单、直接、紧凑。
  • 在双方都不太容易修改的时候再使用适配器模式适配。
posted @ 2019-08-04 16:37  callmeDevil  阅读(370)  评论(0编辑  收藏  举报