结构模式之适配器模式

编程

2 适配器模式

充电器(电源适配器)是日常生活中常见的例子。大多手机要求输入电压是5V,而家用交流电的电压都是220V,充电器作为适配器,将220V的电压转为目标电器需要的电压。适配器模式也类似,通过适配器,将类的接口转换为目标所期望的另一个接口。

适配器模式是开闭原则的体现,通过增加一个适配类,避免了对原有逻辑的修改。

3 案例

适配器模式主要有三种类型:类适配器,对象适配器,默认适配器。下面举例说明。

3.1 类适配器

类适配器使用的是继承模式,适配器类即是A接口,又是B接口。下面的例子展现了,如何通过适配器,使得“中国人”说“英语”:

public class Test {

public static void main(String[] args) {

// 普通ChineseSpeaker对象,只能输出中文

ChineseSpeaker xiaoming = new ChinesePeople();

xiaoming.speak();

// 被适配过的ChineseSpeaker对象,可以输出英文

ChineseSpeaker englishTeacher = new ChinesePeopleEnglishAdapter();

englishTeacher.speak();

}

}

public interface EnglishSpeaker {

void talk();

}

public class EnglishPeople implements EnglishSpeaker {

[@Override](https://my.oschina.net/u/1162528)

public void talk() {

System.out.println("Hello!");

}

}

public interface ChineseSpeaker {

void speak();

}

public class ChinesePeople implements ChineseSpeaker {

[@Override](https://my.oschina.net/u/1162528)

public void speak() {

System.out.println("大家好");

}

}

// 适配器类继承了EnglishPeople,将speakChinese方法适配为speakEnglish方法

public class ChinesePeopleEnglishAdapter extends EnglishPeople implements ChineseSpeaker {

[@Override](https://my.oschina.net/u/1162528)

public void speak() {

this.talk();

}

}

输出:

大家好

Hello!

ChineseSpeakerEnglishSpeaker是两个不同的接口,而通过适配器类ChinesePeopleEnglishAdapter,使得ChineseSpeaker对象可以调用EnglishSpeaker的方法。

3.2 对象适配器

对象适配器使用的是组合模式,适配器实现A接口,并持有B接口的实例。下面的例子展现了,如何通过适配器,使得“普通人“可以“飞行”:

public class Test {

public static void main(String[] args) {

// 普通的Person对象,只能“跑”

Person blackWidow = new Mortal("Natasha");

blackWidow.move();

IronSuit suit = new IronSuit();

// 被适配了的对象,可以实现“飞”

Person ironMan = new FlyablePersonAdapter("Tony Stark", suit);

ironMan.move();

}

}

public interface Person {

void move();

}

public class Mortal implements Person {

private String name;

Mortal(String name) {

this.name = name;

}

[@Override](https://my.oschina.net/u/1162528)

public void move() {

System.out.println(name + " is running!");

}

}

public interface Flyable {

void fly();

}

public class IronSuit implements Flyable {

[@Override](https://my.oschina.net/u/1162528)

public void fly() {

System.out.println("I"m flying!");

}

}

// 适配器类持有IronSuit实例,将move方法适配为fly方法

public class FlyablePersonAdapter implements Person {

private String name;

IronSuit suit;

FlyablePersonAdapter(String name, IronSuit suit) {

this.name = name;

this.suit = suit;

}

@Override

public void move() {

System.out.print(name + " is wearing Iron Suit: ");

suit.fly();

}

}

输出:

Natasha is running!

Tony Stark is wearing Iron Suit: I"m flying!

通过适配,可以让Personmove()方法变为Flyablefly()方法。

3.3 默认适配器

默认适配器是适配器模式的变种,主要解决的问题是,当一个接口有多个方法时,有时候实现类只关心其中的部分方法。通过添加一个适配器类来给方法提供默认实现,可以实现这一需求:

public class Test {

public static void main(String[] args) {

People jay = new Singer();

jay.speak();

People yao = new Athlete();

yao.move();

}

}

public interface People {

void eat();

void sleep();

void move();

void speak();

}

public class PeopleAdapter implements People {

@Override

public void eat() {}

@Override

public void sleep() {}

@Override

public void move() {}

@Override

public void speak() {}

}

// 通过适配器,Athlete只需要实现move方法

public class Athlete extends PeopleAdapter {

@Override

public void move() {

System.out.println("Athlete is running.");

}

}

// 通过适配器,Singer只需要实现speak方法

public class Singer extends PeopleAdapter {

@Override

public void speak() {

System.out.println("Singer is singing.");

}

}

输出:

Singer is singing.

Athlete is running.

适配器类PeopleAdapter给接口中的方法提供了默认的实现(或是空实现),使得其子类可以只关心自己需要的方法。

4 使用

前两种适配器模式,在给系统新增功能的时候非常有用,可以避免对原有逻辑的修改,降低系统的复杂度。比如JDK中我们熟悉的Callable接口,跟Runnable一样,也可以新起一个线程。但这是JDK1.5中新增的接口,而新起线程是由Runnable的实现类Thread中的native方法实现的,那如何在原有基础上,增加对Callable支持呢?答案就是适配器模式:

public class FutureTask<V> implements RunnableFuture<V> {

private Callable<V> callable;

public FutureTask(Callable<V> callable) {

this.callable = callable;

}

public void run() {

Callable<V> c = callable;

if (c != null && state == NEW) {

V result;

boolean ran;

try {

result = c.call();

ran = true;

} catch (Throwable ex) {

result = null;

ran = false;

setException(ex);

}

if (ran)

set(result);

}

}

}

Callable都会被包装成一个FutureTask的实例,FutureTask实现了Runnable接口,可以作为RunnableCallable两个接口的适配器,这样,我们不需要对原先Runnable的框架做任何修改。

而第三种适配器模式则主要运用在开发过程中,可以为我们减少很多工作,易于开发。比较广为人知的便是Netty的ChannelHandlerAdapter,它为开发者提供了接口中方法的空实现,降低了接口使用的难度。

4 总结

适配器模式符合开闭原则。当需要使两个不兼容的接口一起工作时,适配器模式将是很好的选择。

文中例子的github地址

以上是 结构模式之适配器模式 的全部内容, 来源链接: utcz.com/z/515089.html

回到顶部