从王者荣耀看设计模式(适配器模式)
一.简介
在王者荣耀中,英雄元歌具有能够操纵傀儡的能力。元歌可以躲在草丛里,在本体状态使用秘术影来突进目标,同时傀儡会变成敌方英雄的样子,如果双方阵营有一样的英雄,能一定程度上对敌方的判断进行干扰。
二.适配器模式
适配器模式(Adapter Pattern):将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
模式动机
在现实生活中,我们需要存在互不兼容的事物协同工作,在这种情况下可以通过一个名为适配器的设备使原来不兼容的事物一起工作。在软件开发中亦然。在本实例中,元歌可以分别使用鲁班和后羿的适配器从而访问到具体类的方法
- 适配器模式的使用场景
- 系统需要使用现有的类,而这些类的接口不符合系统的需要
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作![]
+ 适配器模式涉及的设计原则有:
1. 针对接口编程,而不是针对实现编程(由于java不支持多继承,类适配和对象适配都是实现接口)
2. 为了交互对象之间的松耦合设计而努力
3. 一个类应该只有一个引起变化的原因
4. 类适配器继承适配者类,实现目标抽象类;对象适配器实现目标抽象类,调用适配者类对象
+ 适配器模式所涉及的角色有:
**Target(目标抽象类)**:目标抽象类定义客户要用的特定领域的接口,可以是个抽象类或接口,也可以是具体类;在类适配器中,由于java语句不支持多重继承,它只能是接口。
**Adapter(适配器类)**:适配器类可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。适配器Adapter是适配器模式的核心,在类适配器中,它通过实现Target接口并继承Adaptee类来使二者产生联系,在对象适配器中,它通过继承/实现Target并关联一个Adaptee对象使二者产生联系。
**Adaptee(适配者类)**:适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下甚至没有适配者类的源代码。
**Client(客户类)**:在客户类中针对目标抽象类进行编程,调用在目标抽象类中定义的业务方法。
+ 适配器模式的优点:
1. 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
2. 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
3. 由于适配器类是适配者类的子类,因此可以在适配器类中配置一些适配者的方法,使得适配器的灵活性更强
4. 对象适配器可以把多个不同的适配者适配到同一个目标
+ 适配器模式的缺点:
1. 类适配器:对于java,C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性,不能将一个适配者类和它的子类同时适配到目标接口。
2. 对象适配器:与类适配器相比,要想置换适配者类的方法就不容易(如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较复杂)
### 三.适配器模式(类适配器)
##### 3.1 类适配器通用类图
![](https://img2018.cnblogs.com/blog/1597476/201911/1597476-20191130094512128-721682309.jpg)
##### 3.2 结构图(类适配器)
![](https://img2018.cnblogs.com/blog/1597476/201911/1597476-20191130094654891-191840298.png)
##### 3.3 设计类图
![](https://img2018.cnblogs.com/blog/1597476/201911/1597476-20191130094726519-561041702.jpg)
##### 3.4 代码实现
***YuanGe类(英雄元歌,持有接口ISKill)***
```
package com.practice.Target;
import com.practice.Adapter.HouYiAdapter;
import com.practice.Adapter.LuBanQiHaoAdapter;
/*
* 英雄元歌类,持有Target接口
*/
public class YuanGe{
String heroName;
ISkill skill1 = null;
public void setHero(String heroName) {
this.heroName = heroName;
}
//选择相应的适配器
public void useSkill() throws Exception{
//System.out.println(heroName.getHeroName());
if(heroName.equalsIgnoreCase("HouYi")) {
System.out.print("[元歌]");
skill1=new HouYiAdapter();
}else if(heroName.equalsIgnoreCase("LuBanQiHao")) {
System.out.print("[元歌]");
skill1=new LuBanQiHaoAdapter();
}else {
throw new Exception();
}
//使用适配器方法
skill1.useSkill();
}
}
```
***ISkill类(Target类)***
```
package com.practice.Target;
/*
* Target类
*/
public interface ISkill {
public void useSkill();
}
```
***HouYiAdapter类(HouYi类的适配器)***
```
package com.practice.Adapter;
import com.practice.Domain.HouYi;
import com.practice.Target.ISkill;
/*
* Adapter类:实现接口继承HouYi类(以便调用HouYi类的相关方法)
*/
public class HouYiAdapter extends HouYi implements ISkill {
//实现接口的方法
public void useSkill() {
//调用Adaptee类中的目标方法
display();
}
}
```
***LuBanQiHaoAdapter类(鲁班七号的适配器)***
```
package com.practice.Adapter;
import com.practice.Domain.LuBanQiHao;
import com.practice.Target.ISkill;
/*
* Adapter类:实现接口继承LuBanQiHao类(以便调用LuBanQiHao类的相关方法)
*/
public class LuBanQiHaoAdapter extends LuBanQiHao implements ISkill {
//实现接口的方法
public void useSkill() {
//调用Adaptee类中的目标方法
display();
}
}
```
***Hero类(Adaptee类的父类)***
```
package com.practice.Domain;
/*
* Hero类
* @heroName 英雄的名字
*/
public abstract class Hero {
private String heroName;
//英雄的自我介绍
public abstract void display();
public String getHeroName() {
return heroName;
}
public void setHeroName(String heroName) {
this.heroName = heroName;
}
}
```
***HouYi类(Adaptee类)***
```
package com.practice.Domain;
/*
* Adaptee类,包含客户想要实现的方法
*/
public class HouYi extends Hero {
public HouYi() {
super.setHeroName("后羿");
}
//客户想要实现的方法
public void display() {
System.out.println("后羿:苏醒吧,猎杀时刻!");
}
}
```
***LuBanQiHao类(Adaptee类)***
```
package com.practice.Domain;
/*
* Adaptee类,包含客户想要实现的方法
*/
public class LuBanQiHao extends Hero {
public LuBanQiHao() {
super.setHeroName("鲁班七号");
}
//客户想要实现的方法
public void display() {
System.out.println("鲁班:相信科学!");
}
}
```
***AdapterTest类(Client测试)***
```
package com.practice.Client;
import com.practice.Domain.Hero;
import com.practice.Domain.HouYi;
import com.practice.Domain.LuBanQiHao;
import com.practice.Target.YuanGe;
/*
* 测试类Test
*/
public class AdapterTest {
public static void main(String[] args) {
//测试Adaptee类中的方法
Hero HouYi = new HouYi();
HouYi.display();
Hero LuBan = new LuBanQiHao();
LuBan.display();
//创建英雄元歌
YuanGe YuanGe = new YuanGe();
try {
//选择适配的英雄
YuanGe.setHero("HouYi");
//使用适配英雄的方法
YuanGe.useSkill();
YuanGe.setHero("LuBanQiHao");
YuanGe.useSkill();
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
##### 3.5 运行结果
![](https://img2018.cnblogs.com/blog/1597476/201911/1597476-20191130095634760-484125177.png)
______
### 四.适配器模式(对象适配器)
##### 4.1 对象适配器通用类图
![](https://img2018.cnblogs.com/blog/1597476/201911/1597476-20191130095706112-591163489.jpg)
##### 4.2 结构图(对象适配器)
![](https://img2018.cnblogs.com/blog/1597476/201911/1597476-20191130095732220-1206231618.png)
##### 4.3 设计类图
![](https://img2018.cnblogs.com/blog/1597476/201911/1597476-20191130095851967-1247841638.jpg)
##### 4.4 代码实现
***YuanGe类(持有Target接口)***
```
package com.practice.Target;
import com.practice.Adapter.Adapter;
import com.practice.Domain.Hero;
/*
* 英雄元歌类,持有Target接口
* @Hero heroName 接收传入的Adaptee对象
*/
public class YuanGe{
Hero heroName;
ISkill skill1 = null;
public void setHero(Hero heroName) {
this.heroName = heroName;
}
public void useSkill() throws Exception{
//System.out.println(heroName.getHeroName());
if(heroName.getHeroName().equalsIgnoreCase("后羿")) {
System.out.print("[元歌]");
skill1=new Adapter(heroName);
}else if(heroName.getHeroName().equalsIgnoreCase("鲁班七号")) {
System.out.print("[元歌]");
skill1=new Adapter(heroName);
}else {
throw new Exception();
}
skill1.useSkill();
}
}
```
***ISkill接口(Target接口)***
```
package com.practice.Target;
/*
* Target类
*/
public interface ISkill {
public void useSkill();
}
```
***Adapter类(适配器类)***
```
package com.practice.Adapter;
import com.practice.Domain.Hero;
import com.practice.Target.ISkill;
/*
* Adapter类,把传入Adaptee对象,封装对象的方法
*/
public class Adapter implements ISkill {
Hero hero = null;
public Adapter(Hero hero) {
this.hero = hero;
}
public void useSkill() {
hero.display();
}
}
```
***HouYi类(Adaptee类)***
```
package com.practice.Adaptee;
import com.practice.Domain.Hero;
/*
* Adaptee类,包含客户想要实现的方法
*/
public class HouYi extends Hero {
public HouYi() {
super.setHeroName("后羿");
}
//客户想要实现的方法
public void display() {
System.out.println("后羿:苏醒吧,猎杀时刻!");
}
}
```
***LuBanQiHao类(Adaptee类)***
```
package com.practice.Adaptee;
import com.practice.Domain.Hero;
/*
* Adaptee类,包含客户想要实现的方法
*/
public class LuBanQiHao extends Hero {
public LuBanQiHao() {
super.setHeroName("鲁班七号");
}
//客户想要实现的方法
public void display() {
System.out.println("鲁班:相信科学!");
}
}
```
***Hero类(Adaptee类的父类)***
```
package com.practice.Domain;
/*
* Hero类
* @heroName 英雄的名字
*/
public abstract class Hero {
private String heroName;
//英雄的自我介绍
public abstract void display();
public String getHeroName() {
return heroName;
}
public void setHeroName(String heroName) {
this.heroName = heroName;
}
}
```
***AdapterTest类(Client测试类)***
```
package com.practice.Client;
import com.practice.Adaptee.HouYi;
import com.practice.Adaptee.LuBanQiHao;
import com.practice.Domain.Hero;
import com.practice.Target.YuanGe;
/*
* 测试类Test
*/
public class AdapterTest {
public static void main(String[] args) {
//测试Adaptee类中的方法
Hero HouYi = new HouYi();
HouYi.display();
Hero LuBan = new LuBanQiHao();
LuBan.display();
//创建英雄元歌
YuanGe YuanGe = new YuanGe();
try {
//选择适配的英雄
YuanGe.setHero(new LuBanQiHao());
YuanGe.useSkill();
//使用适配英雄的方法
YuanGe.setHero(new HouYi());
YuanGe.useSkill();
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
##### 4.5 运行结果
![](https://img2018.cnblogs.com/blog/1597476/201911/1597476-20191130100601310-2013652352.png)
#### 五.源代码下载
[从王者荣耀看设计模式(适配器模式)](https://github.com/miaowu169/Adapter-Pattern);