前言
本节学习结构型中的适配器模式。
适配器模式将一个类的接口,转换成客户期望的另一个接口。
适配器类似我们生活中将安卓充电头转化为苹果充电头的转化器一样。适配另一方的功能,使得两方能亲密无间的合作。
代码实现
关键:适配器实现目标接口,并持有适配者的实例。
代码实现将 "一只鸡" 适配成 "一只鸭子"
/**
* 目标接口 想要适配成的接口
*/
public interface Duck {
void talk();
void fly();
}
/**
* 目标接口的一个实现类 事实上可以不提供此类,
* 只不过看到这个类可以更好的理解面向接口编程。
*/
public class DemoDuck implements Duck {
@Override
public void talk() {
System.out.println("demo duck talk");
}
@Override
public void fly() {
System.out.println("demo duck fly");
}
}
/**
* 被适配的接口
*/
public interface Chicken {
void hi();
void fly();
}
/**
* 被适配者
*/
public class BlackChicken implements Chicken {
@Override
public void hi() {
System.out.println("black chicken say");
}
@Override
public void fly() {
System.out.println("black chicken fly ");
}
}
/**
* 适配器 实现目标接口
*/
public class ChickenAdapter implements Duck {
// 持有被适配者对象
// 组合
private Chicken chicken;
// 通过构造器传入被适配对象
public ChickenAdapter(Chicken chicken) {
this.chicken = chicken;
}
// 适配器把适配者接口转换成被适配者的一个或者多个调用接口
@Override
public void talk() {
this.chicken.hi();
}
// chicken 适配成duck 需要飞两次
@Override
public void fly() {
this.chicken.fly();
this.chicken.fly();
}
}
/**
* 测试类
*/
public class AdapterTest {
public static void main(String[] args) {
// new 被适配者
BlackChicken blackChicken = new BlackChicken();
System.out.println("未适配前");
blackChicken.fly();
blackChicken.hi();
System.out.println("适配后");
// 将被适配者通过构造器
// 注意 此时已经适配成了 Duck 接口
Duck chickenAdapter = new ChickenAdapter(blackChicken);
chickenAdapter.fly();
chickenAdapter.talk();
}
}
需要注意的点:
- 面向接口编程
- 可以看到适配器是针对目标接口 和 被适配者的接口来 进行编程的。
- 组合的方式 而不是继承。适配器持有被适配者对象
UML
多说一句,如果想要将鸡适配成鸟的接口,而鸟会飞,鸡不会飞 。对于鸟的 fly() 方法怎么做呢? 可以通过抛出不支持的异常给客户端。
类的适配器:
上面的方式是对于对象的适配,通过组合的方式的。事实上如果对于类的适配需要多继承才能实现,而Java是不支持多继承的。所以你只需要了解有这样的东西即可。
类适配通过继承目标类和被适配者。
类适配器和对象适配器的比较:
- 对象适配器以组合的方式 更有弹性,让子类对象相互协作
- 不需要重新实现整个被适配者(通过继承,像是将两个类绑定到一起)。可以覆盖被适配者的行为。
和装饰者的比较,和门面模式的比较
- 装饰者侧重于增强目标对象,包装对象的行为或责任
- 适配器模式主要侧重将一个接口 "转换" 成另一个接口
- 门面模式侧重将多个子系统组合在一起。给外部提供一个统一接口。让外部不关注那些子系统,而是通过外观给的接口就能调用子系统了。展示了组合的魅力。
事实上如果对于它们的定义更加清晰也就更清晰了。
References
- 《Head First 设计模式》