1.前言

继上一讲IOC模式的基础上继续本讲桥接模式,笔者感觉桥接模式是23种设计模式中桥接模式是最好用但也是最难理解的设计模式之一,23中设计模式就好武侠剧中一本武功秘籍,我们在工作过程中想要熟练运用其中的每一种设计模式就好比跟高手过招想要能运用好武侠秘籍中的每一招每一式,并且能随着对手出招的不同我们能随机应变对应的招数,这就要求我们对每一种设计模式都理解的非常深刻才能运用自如,打出组合拳的效果。

2.需求

我们在FPS类游戏中会碰到这样的需求——实现武器和角色,无论是敌人还是我方角色都能通过不同的武器击杀对方,武器有手枪、散弹枪、以及火箭炮,并以”攻击力”和”攻击距离”来区分他们的威力。我们可能会这样实现: 
 
上面是我们的一个UML示例图,这里笔者很惭愧没用过UML专业的绘图工具,就临时用绘图软件简单的画了一下。下面就是我们的初步设计代码。

  • 角色基类
public abstract class ICharacter
{
//拥有一把武器
protected Weapon m_Weapon = null;
//攻击目标
public abstract void Attack(ICharacter target);
}
  • 武器类
using UnityEngine;

public enum ENUM_Weapon
{
Null = ,
Gun,
Rifle,
Rocket,
} public class Weapon
{
protected ENUM_Weapon m_EmEwapon = ENUM_Weapon.Null;
protected int m_AtkValue = ;//攻击力
protected int m_AtkRange = ;//攻击距离
protected int m_AtkPlusValue = ;//额外加成 public Weapon(ENUM_Weapon type, int atkValue, int atkRange)
{
m_EmEwapon = type;
m_AtkValue = atkValue;
m_AtkRange = atkRange;
} public ENUM_Weapon GetWeaponType()
{
return m_EmEwapon;
} public void Fire(ICharacter target)
{
//
} public void SetAtkPlusValue(int atkPlusValue)
{
m_AtkPlusValue = atkPlusValue;
} public void ShowBulletEffect(Vector3 targetPosition, float lineWidth, float displayTime)
{ } public void ShowShootEffect()
{ } public void ShowSoundEffect(string clipName)
{ }
}
  • 敌人使用武器
using System.Collections;
using System.Collections.Generic;
using UnityEngine; public class IEnemy : ICharacter
{
public IEnemy()
{ } public override void Attack(ICharacter target)
{
m_Weapon.ShowShootEffect();
int atkPlusValue = ;
switch(m_Weapon.GetWeaponType())
{
case ENUM_Weapon.Gun:
//显示武器特效
m_Weapon.ShowBulletEffect(target.GetPosition(), 0.3f, 0.2f);
m_Weapon.ShowSoundEffect("GunShot");
atkPlusValue = GetAtkPluginValue(, );
break;
case ENUM_Weapon.Rifle:
m_Weapon.ShowBulletEffect(target.GetPosition(), 0.4f, 0.2f);
m_Weapon.ShowSoundEffect("GunShot");
atkPlusValue = GetAtkPluginValue(, );
break;
case ENUM_Weapon.Rocket:
m_Weapon.ShowBulletEffect(target.GetPosition(), 0.5f, 0.2f);
m_Weapon.ShowSoundEffect("GunShot");
atkPlusValue = GetAtkPluginValue(, );
break;
}
m_Weapon.SetAtkPlusValue(atkPlusValue);
m_Weapon.Fire(target);
}
private int GetAtkPlusValue(int rate, int atkValue)
{
int randValue = UnityEngine.Random.Range(, );
if (rate > randValue)
return atkValue;
return ;
}
}
  • 玩家使用武器
using System.Collections;
using System.Collections.Generic;
using UnityEngine; public class ISoldier : ICharacter
{
public ISoldier()
{ } public override void Attack(ICharacter target)
{
m_Weapon.ShowShootEffect();
switch(m_Weapon.GetWeaponType())
{
case ENUM_Weapon.Gun:
m_Weapon.ShowBulletEffect(target.GetPosition(), 0.03f, 0.2f);
m_Weapon.ShowSoundEffect("GunShot");
break;
case ENUM_Weapon.Rifle:
m_Weapon.ShowBulletEffect(target.GetPosition(), 0.5f, 0.2f);
m_Weapon.ShowSoundEffect("RifleShot");
break;
case ENUM_Weapon.Rocket:
m_Weapon.ShowBulletEffect(target.GetPosition(), 0.8f, 0.2f);
m_Weapon.ShowSoundEffect("RocketShot");
break;
}
m_Weapon.Fire(target);
}
}

3.初步分析

以上实现存在明显两个缺点:

  • 每当添加一个角色时,继承自ICharacter接口的角色重新定义Attack都必须针对不同的武器进行判断,并且要写重复的相同的代码。
  • 每当新增武器时,所有角色的Attack都需要重新改造,这样增加维护成本。 
    为了解决以上问题,下面我们桥接模式隆重登场了!

4.桥接模式

桥接模式(Bridge Pattern):桥接模式的用意是将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。 
以上是桥接模式的官方定义,我们还是通过以上例子来理解分析,”桥接”顾名思义就是这一组跟那一组两组对象通过某种中间关系链接在一起。”当两个群组因为功能上的需求,想要进行链接合作,但又希望两组类可以自行发展互相不受对方变化而影响”,上面角色跟武器的实现案例,武器和角色分别是两组群组,其实上面的实现只是考虑了角色通过子类通过基类继承来实现,然后传入武器对象,这中实现思路有点上一讲介绍的控制反转的味道,那既然角色可以这样设计,为何不把武器也这样设计呢,然后两个群组之间就通过两个群组的接口来实现,就好比两家结婚,双方都有一个媒人来传递信息,这是我对桥接模式的理解,哈哈。基于这种想法,我们来改造一下角色和武器的实现。 

说明:

  • ICharacter:角色的抽象接口拥有一个IWeapon的对象引用,并在接口中申明了一个武器攻击目标WeaponAttackTarget()方法让子类可以调用,同时要求继承子类必须在Attack()中重新实现攻击目标的功能。
  • ISoldier、IEnemy:双方阵容单位实现攻击目标Attack()时,只需要调用父类的WeaponAttackTarget方法就可以使用当前武器攻击对手。
  • IWeapon:武器接口,定义游戏中对于武器的操作和使用方法。
  • WeaponGun、WeaponRifle、WeaponRocket:游戏中可以使用三中武器对象。

5.桥接模式实现武器和角色功能

  • 武器接口
using System.Collections;
using System.Collections.Generic;
using UnityEngine; public abstract class IWeapon
{
//属性
protected int m_AtkPlusValue = ;
protected int m_Atk = ;
protected float m_Range = ; protected GameObject m_GameObject = null;
protected ICharacter m_WeaponOwner = null;//武器拥有者 //发射特效
protected float m_EffectDisplayTime = ;
protected ParticleSystem m_Particles;
protected AudioSource m_Audio; //显示子弹特效
protected void ShowBulletEffect(Vector3 targetPosition, float disPlayTime)
{
m_EffectDisplayTime = disPlayTime;
} //显示枪口特效
protected void ShowShootEffect()
{
if(m_Particles != null)
{
m_Particles.Stop();
m_Particles.Play();
}
} //显示音效
protected void ShowSoundEffect(string clipName)
{
if (m_Audio == null)
return;
IAssetFactory factory = Factory.GetAssetFactory();
var clip = factory.LoadAudioClip(clipName);
if (clip == null)
return;
m_Audio.clip = clip;
m_Audio.Play();
} //攻击目标
public abstract void Fire(ICharacter target);
}
 
  • 手枪武器的实现
public class WeaponGun:IWeapon
{
public WeaponGun()
{ } public override void Fire(ICharacter target)
{
ShowShootEffect();
ShowBulletEffect(target.GetPosition(), 0.3f, 0.2f);
ShowSoundEffect("GunShot"); target.UnderAttack(m_WeaponOwner);
}
}
 

其他武器同理…

  • 角色接口
using System.Collections;
using System.Collections.Generic;
using UnityEngine; public abstract class ICharacter
{
protected Weapon m_Weapon = null; public void SetWeapon(IWeapon weapon)
{
if (m_Weapon != null)
m_Weapon.Release();
m_Weapon = weapon;
//设置武器拥有者
m_Weapon.SetOwener(this);
} //获取武器
public IWeapon GetWeapon()
{
return m_Weapon;
} protected void SetWeaponAtkPlusValue(int value)
{
m_Weapon.SetAtkPlusValue(value);
} protected void WeaponAttackTarget(ICharacter target)
{
m_Weapon.Fire(target);
} //获取武器攻击力
public void GetAtkValue()
{
return m_Weapon.GetAtkValue();
} /// <summary>
/// 获得攻击距离
/// </summary>
/// <returns></returns>
public float GetAttackRange()
{
return m_Weapon.GetAtkRange();
}
/// <summary>
/// 攻击目标
/// </summary>
/// <param name="target"></param>
public abstract void Attack(ICharacter target);
/// <summary>
/// 被其他角色攻击
/// </summary>
/// <param name="attacker"></param>
public abstract void UnderAttack(ICharacter attacker);
}
 
  • 桥接模式角色实现
//角色接口
public class ISolider : ICharacter
{
public override void Attack(ICharacter target)
{
WeaponAttackTarget(target);
} public override void UnderAttack(ICharacter attacker)
{
...
}
} //Enemy接口
public class IEnemy:ICharacter
{
public override void Attack(ICharacter target)
{
SetWeaponAtkPlusValue(m_Weapon.GetAtkPlusValue);
WeaponAttackTarget(target);
} public override void UnderAttack(ICharacter attacker)
{
...
}
}

4.桥接模式改造后分析

以上设计运用桥接模式后的ICharacter就是群组”抽象类”,它定义了”攻击目标”功能,但实现攻击目标功能的却是群组”IWeapon武器类”,对于ICharacter以及其继承都不会理会IWeapon群组的变化,尤其在新增武器类的时候也不会影响角色类。对于ICharacter来说,它面对的指示IWeapon这个接口类,这让两个群组耦合度降到最低。

5.思考

结合以上案例,可以思考另外一个需求:用不同的图形渲染引擎如OpenGL、DirectX来绘制不同的图形。 
提示:渲染引擎RenderEngine和图形属于两大群体,这两大群体都要单独有各自的“抽象类”抽象类RenderEngine和抽象类Shape。

    • 抽象类RenderEngine肯定要有一个抽象功能方法就是Draw(string shape),这个是被子类具体的渲染引擎重写,因为各自的引擎都有自己独特的渲染方法,所以这个Draw方法就被重写调用各个引擎自己的渲染方法GLRender()和DXRender()。
    • 抽象类Shape肯定也要有一个保护对象RenderEngine和设置这个对象的注入方法SetRenderEngine(RenderEngine engine)以及抽象方法Draw()。Draw方法是要被子类重写实现:调用renderEngine的Draw方法来绘制自己。

[Unity 设计模式]桥接模式(BridgePattern)的更多相关文章

  1. 转:设计模式-----桥接模式(Bridge Pattern)

    转自:http://www.cnblogs.com/houleixx/archive/2008/02/23/1078877.html 记得看原始链接的评论. 学习设计模式也有一段时间了,今天就把我整理 ...

  2. 跟着ZHONGHuan学习设计模式--桥接模式

    转载请注明出处! ! !http://blog.csdn.net/zhonghuan1992 全部配套代码均在github上:https://github.com/ZHONGHuanGit/Desig ...

  3. linkin大话设计模式--桥接模式

    linkin大话设计模式--桥接模式 桥接模式是一种结构化模式,他主要应对的是:由于实际的需要,某个类具有2个或者2个以上维度的变化,如果只是使用继承将无法实现功能,或者会使得设计变得相当的臃肿.我们 ...

  4. java设计模式——桥接模式

    一. 定义与类型 定义:将抽象部分与他的具体实现部分分离,使它们都可以独立的变化,通过组合的方式建立两个类之间的联系,而不是继承 类型:结构性. 二. 使用场景 (1) 抽象和具体实现之间增加更多的灵 ...

  5. 【设计模式】Java设计模式 - 桥接模式

    [设计模式]Java设计模式 - 桥接模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起 ...

  6. JAVA 设计模式 桥接模式

    用途 桥接模式 (Bridge) 将抽象部分与实现部分分离,使它们都可以独立的变化. 桥接模式是一种结构式模式. 结构

  7. 深入浅出设计模式——桥接模式(Bridge Pattern)

    模式动机设想如果要绘制矩形.圆形.椭圆.正方形,我们至少需要4个形状类,但是如果绘制的图形需要具有不同的颜色,如红色.绿色.蓝色等,此时至少有如下两种设计方案: 第一种设计方案是为每一种形状都提供一套 ...

  8. javascript设计模式-桥接模式

    在系统中,某些类由于自身逻辑,具有两个或两个以上维度的变化,如何使得该类型可以沿多个方向变化,但又不引入额外的复杂度,这就是桥接模式要解决的问题. 定义:桥接模式(Bridge),将抽象部分与它的实现 ...

  9. 设计模式 -- 桥接模式(Bridge Pattern)

    桥接模式 Bridge Pattern 结构设计模式 定义: 分离抽象部分和实现部分,使他们独立运行. 避免使用继承导致系统类个数暴增,可以考虑桥接模式. 桥接模式将继承关系转化为关联关系,减少耦合, ...

随机推荐

  1. Python实现单词查询&文件查找

    最近学C++ Primer,做到第十二章有个习题.要求针对英文文本,对于用户想搜索的单词,打印出该单词在文本中出现的总次数,单词所出现行号及对应的行内容:单词在一行内出现多次,只打印该行一次.C++的 ...

  2. 查看ubuntu的内核版本&获取roo…

    1.查看内核版本命令 [root@AY130616190837708391Z ~]# cd .. (管理员权限) [root@AY130616190837708391Z /]# cd proc [ro ...

  3. akoj-1074-人见人爱A^B

    人见人爱A^B Time Limit:1000MS  Memory Limit:65536K Total Submit:91 Accepted:55 Description 求A^B的最后三位数表示的 ...

  4. Hibernate(一)

    1.1Hibernate框架概述 1.1.1什么是Hibernate? Hibernate是轻量级JavaEE应用的持久层解决方案,是一个关系数据库ORM框架. ORM就是通过将Java对象映射到数据 ...

  5. 社交系统/社群系统“ThinkSNS+”H5及PC端终于来了!一起来“找茬”

    [什么是TS+?] ThinkSNS(简称TS),一款全平台综合性社交系统,为国内外大中小企业和创业者提供社会化软件研发及技术解决方案,目前最新版本为ThinkSNS+,简称TS+. 还记得2017年 ...

  6. Gate One

    运维堡垒机介绍: 运维堡垒机的理念起源于跳板机.2000年左右,高端行业用户为了对运维人员的远程登录进行集中管理,会在机房里部署跳板机.跳板机就是一台服务器,维护人员在维护过程中,首先要统一登录到这台 ...

  7. python--注释

    python中单行注释用#,多行注释用""",看下面的代码: #!/usr/bin/python #coding:utf-8 #编码方式 #打印一行* print &qu ...

  8. ORACLE概要文件

    oracle系统为了合理分配和使用系统的资源提出了概要文件的概念.所谓概要文件,就是一份描述如何使用系统的资源(主要是CPU资源)的配置文件.将概要文件赋予某个数据库用户,在用户连接并访问数据库服务器 ...

  9. 3_yum rpm tar 命令及参数

    这个就是把上课听课时写下的笔记给复制粘贴过来了,如果写的不够详细或者哪里不明白,可以写在评论下面,我会一一个回复的:   1.yum centos 上 一般是yum  提供了安装包的查找安装及其删除  ...

  10. CY7C68013A控制传输

    大家好,你们的大熊又回来了.本篇文章我们来重点了解一下USB设备的四大传输方式之一--控制传输.不同于其他三种传输方式,控制传输有其独特的作用和功能,是一个USB设备必须支持的传输方式.控制传输对带宽 ...