1、模式标准
模式名称:桥接模式
模式分类:结构型
模式意图:将抽象部分与其实现部分分离,使它们都可以独立地变化。
结构图:
适用于:
1、不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如,这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
2、类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这是 Bridge 模式使得开发者可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
3、对一个抽象的实现部分的修改应对客户不产生影响,即客户代码不必重新编译
4、想在多个对象间共享实现 (可能使用引用计数),但同时要求客户并不知道这一点
主要成员:
- 抽象类(Abstraction):定义和保存客户端的引用,维护与实现对象之间的关联关系。
- 扩展抽象类(Refined Abstraction):扩展和精化抽象类中的接口定义。
- 实现类接口(Implementor):定义实现类的接口,可以与抽象类的接口相互独立。
- 具体实现类(Concrete Implementor):具体实现接口,在不同的实现类中提供基本操作的不同实现。
2、分析与设计
桥接模式,通俗点讲就如同嫁接,比如有一个树,会开花结果,我给它嫁接苹果枝,在开花结果后,长的是苹果。如果嫁接橘子枝,在开花结果后,长的是橘子。
在游戏开发中,可能会用在角色的武器切换上,不同的武器拥有不同的攻击方式。拥有剑就拥有了剑的攻击效果,拥有弓箭就拥有弓的攻击效果。下面我们来描述一下新的意图
意图:将抽象部分(攻击)与其实现部分(剑攻击还是弓箭攻击)分离,使它们都可以独立地变化(剑有剑的攻击效果,弓有弓的攻击效果)。
当前的例子用在角色的攻击方式的设计上。下面我们来描述一下新的意图
意图:将抽象部分(攻击)与其实现部分(近程攻击还是远程攻击)分离,使它们都可以独立地变化(近程的攻击立即生效,远程的攻击等抛射物接触到单位攻击生效)。
3、开始打造
上图的第一版的结构图不太标准(因为桥接模式属于结构性模式,抽象与实现可以独立地变化,并在编译时通过组合的方式耦合在一起。而第一版中将setWeapon加进去,武器是看上去是一种可以动态替换的,会和后面的策略模式搞混)。下图是新的将“攻击方式”代替武器,因为攻击方式一般在角色构建好后就定了,不会随意切换。(后面的策略模式会引入武器,实现策略模式和桥接模式混用)
// 攻击方式接口
export interface IAttackStrategy {
attack(): void;
}
// 近程攻击
export class MeleeAttack implements IAttackStrategy {
attack(): void {
console.log("进行近程攻击:");
}
}
// 远程攻击
export class RangedAttack implements IAttackStrategy {
attack(): void {
console.log("进行远程攻击:");
}
}
// 抽象战斗者
export abstract class Fighter {
protected attackStrategy: IAttackStrategy;
constructor(attackStrategy: IAttackStrategy) {
this.attackStrategy = attackStrategy;
}
attack(): void {
this.attackStrategy.attack();
}
}
// 骑兵
export class Cavalry extends Fighter {
constructor(attackStrategy: IAttackStrategy) {
super(attackStrategy);
}
}
// 弓箭手
export class Archer extends Fighter {
constructor(attackStrategy: IAttackStrategy) {
super(attackStrategy);
}
}
4、开始使用
export class UnitItem extends Component implements IItem, IUnitItem {
private ad: number = 100;
private role: Fighter;
accept(visitor: IAttackVisitor) {
visitor.visitUnitItem(this)
}
async attack(unitItem: UnitItem<T>) {
this.role.attack(); // 攻击
let damage = this.ad
let attackVisitor = new MonomerAttackVisitor(damage)
unitItem.accept(attackVisitor)
}
setRole(role: Fighter): void {
this.role = role;
}
}
let unitItem001 = xhgame.itemFactory.createUnitItem('shibing_001')
let unitItem002 = xhgame.itemFactory.createUnitItem('shibing_002')
unitItem001.setRole(new Cavalry(new MeleeAttack()));
console.log('unitItem001(骑兵-近程)对unitItem002发起了攻击')
unitItem001.attack(unitItem002)
unitItem002.setRole(new Cavalry(new RangedAttack()));
console.log('unitItem002(骑兵-远程)对unitItem001发起了攻击')
unitItem002.attack(unitItem001)