人物:大鸟,小菜
事件:大鸟和小菜看NBA,讨论姚明,说姚明这几年不但球技大涨,而且也能用英语顺利交流了,最开始去NBA时还需要翻译,才能和队友教练沟通。大鸟这时想到了适配器模式,并传授给了小菜
适配器模式:
1.简介适配器模式
2.用代码实现教练指挥打篮球
3.用适配器模式帮助姚明翻译英语,听懂战术
4.举例加深印象 --魏文王问扁鹊
适配器模式
1.概念:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
2.举例:姚明的翻译就是适配器
3.适配器模式讲了两种类型:类适配器模式和对象适配器模式
4.适配器结构图:
5.适配器模式代码:
Target类,这是客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口:
@Slf4j public class Target { public void request() { log.info("普通请求!"); } }
Adaptee类,需要适配的类:
@Slf4j public class Adaptee { public void specificRequest() { log.info("特殊请求!"); } }
Adapter类,通常在内部包装一个Adaptee对象,把源接口转换为目标接口:
public class Adapter extends Target { private Adaptee adaptee = new Adaptee(); @Override public void request() { adaptee.specificRequest(); } }
客户端代码:
public class AdapterClient { public static void main(String[] args) { Target target = new Adapter(); target.request(); Target aim = new Target(); aim.request(); } }
输出结果:
特殊请求!
普通请求!
6.什么时候使用适配器模式?
答:两个类做的事情相同或相似,但具有不同的接口时可以使用,而且前提是在双方都不太容易修改的情况下才使用(有点亡羊补牢的感觉)
篮球翻译适配器
球员:
public abstract class Player { protected String name; public Player(String name) { this.name = name; } public abstract void attack(); public abstract void defense(); }
后卫,中锋,前锋类
@Slf4j public class Guards extends Player { public Guards(String name) { super(name); } @Override public void attack() { log.info("后卫{}, 进攻", name); } @Override public void defense() { log.info("后卫{}, 防守", name); } }
@Slf4j public class Center extends Player { public Center(String name) { super(name); } @Override public void attack() { log.info("中锋{}, 进攻", name); } @Override public void defense() { log.info("中锋{}, 防守", name); } }
@Slf4j public class Forwards extends Player { public Forwards(String name) { super(name); } @Override public void attack() { log.info("前锋{}, 进攻", name); } @Override public void defense() { log.info("前锋{}, 防守", name); } }
客户端代码:
public class AdapterClient { public static void main(String[] args) { Player b = new Forwards("巴蒂尔"); b.attack(); Player m = new Guards("麦克格雷迪"); m.attack(); Player ym = new Center("姚明"); ym.attack(); ym.defense(); } }
大鸟:注意,姚明刚来NBA,还听不懂英语,无法和教练进行战术沟通,不知道attack和defense的意思,现在就需要改动代码进行适配
适配后的篮球队
新加外籍中锋类:
@Data @Slf4j public class ForeignCenter { private String name; //这里表明'外籍中锋'只懂得中文'进攻' public void 进攻() { log.info("外籍中锋{} 进攻", name); } //这里表明'外籍中锋'只懂得中文'防守' public void 防守() { log.info("外籍中锋{}, 防守", name); } }
翻译者类(即进行适配):
public class Translator extends Player { private ForeignCenter wjzf = new ForeignCenter(); public Translator(String name) { super(name); wjzf.setName(name); } @Override public void attack() { wjzf.进攻(); } @Override public void defense() { wjzf.防守(); } }
客户端代码:
public class AdapterClient { 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(); } }
加深印象
最后借大话设计模式的举例来加深印象(例子很生动):
魏文王问名医扁鹊说:“你们家兄弟三人,都精于医术,到底哪一位最好呢?”
扁鹊答:“长兄最好,中兄次之,我最差。”文王再问:“那么为什么你最出名呢?”扁鹊答:“长兄治病,是治病于病情发作之前。由于一般人不知道他事先能铲除病因,所以他的名气无法传出去;中兄治病,是治病于病情初起时。一般人以为他只能治轻微的小病,所以他的名气只及本乡里。而我是治病于病情严重之时。一般人都看到我在经脉上穿针管放血、在皮肤上敷药等大手术,所以以为我的医术高明,名气因此响遍全国。”
编程同理,如果能事先预防接口不同或不匹配的问题,那么问题就不会发生;有小的问题不匹配时及时重构,问题也不至于扩大;只有碰到无法改变原有设计和代码的情况下时,才考虑适配。事后控制不如事中控制,事中控制不如事前控制,如果能事前控制,又何必去弥补呢。注意,适配器模式固然是好模式,如果无视它的应用场合而盲目使用,就是本末倒置了。