设计模式:适配器模式
在计算机编程中,适配器模式(Adapter Pattern)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
在GOF的设计模式中,对适配器模式讲了两种类型,类适配器模式和对象适配器模式,由于JAVA语言不支持多重继承,我们这里主要讲的是对象适配器模式。
组件
- 目标(Target)角色Target类:客户端所期待得到的接口,
- 适配器(Adapter)角色Adapter类:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
- 源(Adaptee)角色被适配者Adaptee类:现在需要被适配的接口
JAVA源码:
package AdapterPattern;
import static Tools.Print.*;
//The interface the client expects can be abstract class or concrete class
abstract class Target {
public abstract void Request();
}
//Classes that need to be adapted
class Adaptee {
public void SpecificRequest() {
println("Specific Request.");
}
}
//Internally wrapped [Adaptee] objects, converting the [Adaptee] into the target interface
class Adapter extends Target {
private Adaptee adaptee = new Adaptee();
@Override
public void Request() {
adaptee.SpecificRequest();
}
}
public class AdapterTestClass {
public static void main(String[] args) {
Target tar = new Adapter();
tar.Request();
}
}
两个类所做事情相同或相似时,但是具有不同接口时可以使用它,适配器模式更有些“亡羊补牢”的感觉,遇到接口不同时,比如上图Adaptee类与Target类存在不同接口,首先应该考虑重构!!重构!!重构!!,仅仅当双方都难以修改重构的情况下,才可以考虑使用适配器模式。
例子
我们选取《大话设计模式》中篮球翻译适配器为例,NBA篮球手中存在前锋,中锋,后卫等几个球场角色,此时正好出现了一个CBA的中锋队员加入球队,那么他们的语言交流肯定就会出现问题,这时候翻译者(Adapter)就起到适配器者的角色,这个CBA球队队员就是被适配器者(Adaptee),我们希望他能和NBA中锋队员(Target)一样参与合作比赛,我们首先看一下类图:
组件
- 目标(Target)角色Center类(Player类)
- 适配器(Adapter)角色Translator类
- 源(Adaptee)角色被适配者ForeignCenter类
JAVA源码:
package AdapterPattern;
import static Tools.Print.*;
abstract class Player {
protected String name;
public Player(String name) {
this.name = name;
}
public abstract void attack();
public abstract void defense();
}
class Forwards extends Player {
public Forwards(String name) {
super(name);
}
@Override
public void attack() {
println(super.name + " Forwards Attack.");
}
@Override
public void defense() {
println(super.name + " Forwards Defense.");
}
}
class Center extends Player {
public Center(String name) {
super(name);
}
@Override
public void attack() {
println(super.name + " Center Attack.");
}
@Override
public void defense() {
println(super.name + " Center Defense.");
}
}
class Guards extends Player {
public Guards(String name) {
super(name);
}
@Override
public void attack() {
println(super.name + " Guards Attack.");
}
@Override
public void defense() {
println(super.name + " Guards Defense.");
}
}
//Classes that need to be adapted
class ForeignCenter {
private String name;
public ForeignCenter(String name) {
this.name = name;
}
public void foreignAttack() {
println(name + " ForeignGuards Attack.");
}
public void foreignDefense() {
println(name + " ForeignGuards Defense.");
}
}
//adapted
class Translator extends Player {
private ForeignCenter foreCent;
public Translator(String name) {
super(name);
foreCent = new ForeignCenter(name);
}
@Override
public void attack() {
foreCent.foreignAttack();
}
@Override
public void defense() {
foreCent.foreignDefense();
}
}
public class PatternClass {
public static void main(String[] args) {
Player p1 = new Translator("YaoMing");
p1.attack();
p1.defense();
Player p2 = new Guards("LeBron James");
p2.attack();
p2.defense();
}
}
适配器的优点:
-
将目标类和适配者类解耦。
-
增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
-
灵活性和扩展性都非常好,符合开闭-原则。
适配器是否可以随意使用?
过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是ForeignCenter类接口,其实内部被适配成了Player类接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构!!