GoF说明:
抽象与实现分离,使二者可以独立变化
注:GoF的解释有点让人误解,这个说法难道不是 “只依赖接口而不依赖实现的” 的另一种说明,或者是进一步解释依赖倒置原则吗?
我的理解:桥接模式是更高层次的、更复杂的、具有组合性质的抽象设计模式
模式说明:
当我们制作一个功能,这个功能是两个类群组中任意两个实现类组合起来完成的,这个时候我们就可以使用桥接模式对两个类群组的抽象基类进行组合,并实现功能。
案例说明:开发游戏中人物手持武器进行攻击的行为
1.需求说明:游戏内角色或者NPC都可以携带武器进行攻击
2.传统的实现:每个人物的具体实现类中,根据传入的武器类型,攻击时使用分支结构传入不同的参数达到不同武器的攻击效果,
我们来看代码:
public class HeroA{
private Weapon weapon;
public void Attack(WeaponType weaponType){
switch(weaponType){
case WeaponType.GUN:
weapon.playEffect('gun_effect');
weapon.playSound('gun_sound');
break;
case WeaponType.KNIFE:
weapon.playEffect('knife_effect');
weapon.playSound('knife_sound');
break;
}
}
}
以上在没有任何设计模式的情况下的攻击实现流程,每增加一种英雄,以上逻辑就要重写一遍,每增加一种武器,上述分支也要进行修改,繁琐不用多言。
2.使用桥接模式分析:英雄是一个群组,武器是一个群组,功能是英雄群组种的任意一个对象,与武器群组种的任意一个对象,进行组合,并完成攻击功能。
3.实现思路:在英雄基类中,持有一个武器基类。在英雄的抽象攻击方法中,调用武器的抽象攻击方法,完成攻击功能。代码如下:
武器抽象基类:
public abstract class IWeapon{
public virtual void playEffect(){ }
public virtual void playSound(){ }
}
Gun武器子类实现
public class WeaponGun : IWeapon {
public override void playEffect(){
//TODO
//Play Gun Effect
}
public override void playSound(){
//TODO
//Play Gun Sound
}
}
英雄抽象基类:
public abstract class IHero{
public IWeapon mWeapon;
public IHero(IWeapon weapon){
this.setWeapon(weapon);
}
public void setWeapon(IWeapon weapon){
mWeapon = weapon;
}
protected void doAttack(){
mWeapon.playEffect();
mWeapon.playSound();
}
public virtual void Attack(IHero target){ }
}
英雄A具体实现:
public class HeroA : IHero {
//构造方法中指定武器
public HeroA : base (new WeaponGun()) { }
//重写攻击方法
public override void Attack(IHero target){
//指定一些攻击前的行为
//如确定目标是否合法
//当然如果我们能确定,Attck方法不会随着英雄不同而该表,这一步可以写入IHero中。无需子类重写
//武器攻击
doAttack(target);
}
}
至此,我们把两个群组组合的功能,从实际实现上的组合,换到了抽象层面的桥接。代码得到了极大的简洁与解耦。我们开发不同的英雄和不同的武器时,也不需要担心写出垃圾堆一样的代码。团队协作也会得到很好的保护。
我的理解里,桥接模式并不一定时两个群组之间的组合,也可以时三个群组,甚至更多的群组之间的无数种组合。这是不是又能解释GoF说的:抽象与实现分离,使二者可以独立变化。只不过这个时候的抽象与实现,是组合出现的,更加复杂的抽象与实现。