从王者荣耀看设计模式(十七.桥接模式)

从王者荣耀看设计模式(桥接模式)

一.简介

王者荣耀是一款RGB游戏,玩家操控自己选择的英雄进行竞赛。在游戏正式开始前,玩家在挑选想要操作的英雄的同时,也会挑选适合该英雄的召唤师技能。召唤师技能是通用的,每个玩家都可以自由搭配英雄和召唤师技能₍₍◡( ╹◡╹ )◡₎₎

二.模式动机

例:现在有三个英雄,每个英雄可以任意搭配四种召唤师技能,现在有两种设计方案:
设计方式一:

第一种设计方案分析:每个英雄都配备四种召唤师技能,如果需要增加英雄,每增加一个英雄类相应增加四个召唤师技能类,如果需要增加技能,需要修改每一个英雄类和在技能族中添加新技能

设计方式二:

第二种设计方案分析:很明显,对于这种有两个独立变化维度(即有两个变化的原因)的系统,采用方案二进行设计系统中类个数更少,且系统扩展更加方便。设计方案二即为桥接模式。桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量

三.桥接模式

桥接模式(Bridge Pattern):将抽象部分与它的实现部分相分离,使它们都可以独立地变化。它是一种对象结构性模式,又称柄体(Handle and Body)模式或接口(Interface)模式

桥接模式的使用场景:
在以下情况下可以使用桥接模式
■ 如果一个系统需要在构件的抽象化角色和具体角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系
■ 抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合
■ 一个类存在两种独立变化的维度,且这两个维度都需要进行扩展
■ 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用

桥接模式涉及的设计原则有:
★ 封装变化
★ 多用组合,少用继承
★ 针对接口编程,不针对实现编程
★ 为交互对象之间的松耦合设计而努力
★ 类对于扩展开放,对修改关闭

桥接模式通用类图

桥接模式涉及的角色:
桥接模式包含如下角色:
Abstraction(抽象类)
用于定义抽象类的接口,它一般是抽象类而不是抽象接口,其中定义了一个Implementor(实现抽象类)类型的对象并可以维护该对象,它与Implementor之间具有关联关系,它可以包含抽象的业务方法,还可以包含具体的业务方法
RefinedAbstraction(扩充抽象类)
扩充由Abstraction定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在Abstraction中定义的抽象业务方法,在RefinedAbstract中可以调用在Implementor中定义的业务方法
Implementor(实现类接口)
定义实现类的接口,这个接口不一定要与Abstract的接口完全一致,事实上这两个接口可以完全不同,一般来讲,Implementor接口仅提供基本操作。而Abstraction定义的接口可能会做更多更复杂的操作。Implementor接口对这些基本操作进行了定义,而具体实现交给了其子类。通过关联关系,在Abstraction中不仅拥有自己的方法,还可以调用Implementor中定义的方法,使用关联关系来替代继承关系
ConcreteImplementor(具体实现类)
实现Implementor接口并且具体实现它,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,ConcreteImplementor对象将替换其父类对象,提供给客户端具体的业务操作方法。

桥接模式优点:
⑴. 分离抽象接口及其实现部分,使得抽象和实现可以沿着各自的维度来变化
⑵. 桥接模式有时类似于多继承方案。但是多继承方案违背了类的单一职责原则,复用性比较差,而且多继承结构中累的个数非常庞大,桥接模式是比多继承方案更好的解决办法
⑶. 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原系统
⑷. 实现细节对客户透明,可以对用户隐藏实现细节
桥接模式缺点:
⑴. 桥接模式的引入会增加系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
⑵. 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性

我思故我在:桥接模式与策略模式之间的异同?
转载自:(https://blog.csdn.net/youthon/article/details/7653389)

四.结构图

五.设计类图

六.代码实现

创建抽象类(Hero类)

package com.practice.Hero;

import com.practice.Skill.Skill;
/*
 * 创建抽象类
 */
public abstract class Hero {
	Skill skill;
	
	public void setSkill(Skill _skill) {
		this.skill = _skill;
	}
	//定义使用技能的抽象方法
	public abstract void useSkill();
}

创建扩充抽象类(HouYi类)

package com.practice.Hero;
/*
 * 创建扩充抽象类
 */
public class HouYi extends Hero {
	public void useSkill() {
		String heroName = "后羿";
		this.skill.SummonerSkill(heroName);
	}
}

创建扩充抽象类(DaJi类)

package com.practice.Hero;
/*
 * 创建扩充抽象类
 */
public class DaJi extends Hero {
	public void useSkill() {
		String heroName = "妲己";
		this.skill.SummonerSkill(heroName);
	}
}

创建扩充抽象类(XiangYu类)

package com.practice.Hero;
/*
 * 创建扩充抽象类
 */
public class XiangYu extends Hero {
	public void useSkill() {
		String heroName = "项羽";
		this.skill.SummonerSkill(heroName);
	}
}

创建实现类接口(Skill类)

package com.practice.Skill;
/*
 * 创建实现类接口
 */
public interface Skill {
	public void SummonerSkill(String heroName);
}

创建具体实现类(JiPao类)

package com.practice.Skill;
/*
 * 创建具体实现类
 */
public class JiPao implements Skill {
	public void SummonerSkill(String heroName) {
		System.out.println("英雄["+heroName+"]携带召唤师技能——疾跑(100秒CD):增加30%移动速度持续10秒");
	}
}

创建具体实现类(KuangBao类)

package com.practice.Skill;
/*
 * 创建具体实现类
 */
public class KuangBao implements Skill {
	public void SummonerSkill(String heroName) {
		System.out.println("英雄[" + heroName + "]携带召唤师技能——狂暴(60秒CD):增加攻击速度60%,并增加物理攻击力10%持续5秒");
	}
}

创建具体实现类(ZhanSha类)

package com.practice.Skill;
/*
 * 创建具体实现类
 */
public class ZhanSha implements Skill {
	public void SummonerSkill(String heroName) {
		System.out.println("英雄[" + heroName + "]携带召唤师技能——斩杀(90秒CD):立即对身边敌军英雄造成其已损失生命值14%的真实伤害");
	}
}

XML配置文件config.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<className>JiPao</className>
	<className>DaJi</className>
</config>

工具类读取XML文件(XMLUtilGame类)

package com.practice.Client;

import java.io.File;

import javax.xml.parsers.*;
import org.w3c.dom.*;


public class XMLUtilGame {
	//该方法用于从XML配置文件中提取具体类类名。并返回一个实例对象
	public static Object getBean(String args) {
		try {
			//创建文档对象
			DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = dFactory.newDocumentBuilder();
			Document doc;
			doc = builder.parse(new File("src\\com\\practice\\Client\\config.xml"));
			
			//获取包含类名的文本节点
			NodeList nl = doc.getElementsByTagName("className");
			Node classNode = null;
			String cName = null;
			
			if(args.equals("skill")) {
				//获取包含类名的文本节点
				classNode = nl.item(0).getFirstChild();
				cName ="com.practice.Skill." + classNode.getNodeValue();
			}else if(args.equals("hero")) {
				//获取包含类名的文本节点
				classNode = nl.item(1).getFirstChild();
				cName = "com.practice.Hero." + classNode.getNodeValue();
			}
			//通过类名生成实例对象并将其返回
			Class<?> c = Class.forName(cName);
			@SuppressWarnings("deprecation")
			Object obj = c.newInstance();			
			return obj;
		}catch(Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}

创建客户端类(Client类)

package com.practice.Client;

import com.practice.Hero.Hero;
import com.practice.Skill.Skill;

public class Client {
	public static void main(String a[]) {
		Skill skill;
		Hero hero;
		
		skill = (Skill)XMLUtilGame.getBean("skill");
		hero = (Hero)XMLUtilGame.getBean("hero");
		
		hero.setSkill(skill);
		hero.useSkill();
	}
}

运行结果:

七.源代码下载

从王者荣耀看设计模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值