为方便读者,本文已添加至索引:
“魔镜啊魔镜,谁是这个世界上最美丽的人?”月光中,一个低沉的声音回荡在女王的卧室。“是美丽的白雪公主,她正和小霍比特人们幸福快乐地生活在森林之中。”魔镜答道。“可恶!我才应该是最美的人,我要除掉你,白雪公主!”女巫开始用她的水晶球施展起诡异的妖术。
原本安宁的森林最近特别的闹腾,动物们个个都焦躁不安。小霍比特人之一的theWoodcutter(樵夫)在去伐木的路上发现了一头野熊的尸体。这头庞然大物的伤口上除了血淋淋的爪痕,竟然混杂有烧焦的痕迹。他急急忙忙回到住处,和小伙伴们商量对策。他们首先想弄明白的是,到底是什么入侵了自己的家园。
而这个世界的主人--时の魔导士,此刻正倚靠在木制的摇椅上,安详地抽着烟斗。他一回想起自己设计的这个虚拟的童话世界,脸上就露出了满意的笑容。他曾为小霍比特人们建造了武器工坊(见Factory Method笔记),Weapon是一个抽象类,它的子类包括Sword, Dagger, Bow等等都继承了Weapon的方法attack()。attack()能对敌人造成物理上的伤害……嗯,物理上的伤害。“似乎这个还不太完美啊。在魔法的世界里,武器是可以被附魔Enchanted的。”魔导士嘟囔道,“比如,附上火焰的剑能轻易刺穿易燃的装甲,附上寒冰的箭矢也可以用来灭火呀。这想法有趣多了,让我来加点新鲜的东西给孩子们吧。”
附魔后的武器(EnchantedWeapon)的确是可以通过继承Weapon来得到,比如下图:
但是如果这样设计会有很糟糕的结果。武器种类很多,如果日后还要添加别的类型的武器,比如传奇的武器(EpicWeapon)每个都带有史诗般的故事,装饰性的武器(DecoratedWeapon)比起杀伤力更注重外观等等。每添加一种武器分支,都会导致产生更多的子类。同时,如果我们修改了Weapon的接口,那后续改动将非常巨大。
于是,时の魔导士引入了Bridge(桥接)模式,它将抽象部分与实现部分分离,从而很好地解决了上面所提到的问题。
目的分类
范围准则
主要功能
适用情况
参与部分
协作过程
UML图
利用桥接模式,我们将各类武器抽象成Weapon类,它提供一个attack()的接口函数。对于武器的使用者,它只需操纵attack()即可。(发现这点和之前一样,对吧?但是别急)请看示例:
class Weapon {
public:
Weapon();
virtual void attack();
protected:
WeaponImpl* getWeaponImpl();
private:
WeaponImpl* _impl;
}
注意到,Weapon它维护了一个对WeaponImpl的引用,而该抽象类定义了具体武器的操作的接口:
class WeaponImpl {
public:
virtual act() = ;
virtual addEffect(const char*) = ;
protected:
WeaponImpl();
}
现在,我们可以将武器攻击动作的具体实现放在WeaponImpl的子类中,从而成功分离出Weapon类这个抽象概念。作为扩展,附魔武器EnchantedWeapon类如下:
class EnchantedWeapon : public Weapon {
public:
EnchantedWeapon(char*);
virtual void attack();
private:
const char* _element;
}
EnchantedWeapon::EnchantedWeapon(char* e):_element(e) {}
void EnchantedWeapon::attack() {
WeaponImpl* impl = getWeaponImpl();
if (impl != ) {
impl->addEffect(\_element);
impl->act();
}
}
EnchantedWeapon可以为武器附上不同程度的魔法效果,然后通过调用WeaponImpl类的act操作来行动。具体的WeaponImpl类则根据自身情况来实现act操作,剑可以用来挥舞和刺击,弓箭则要上满弦,等等。我们看这里的SwordImpl:
class SwordImpl : public WeaponImpl {
public:
SwordImpl(int);
virtual void addEffect(const char*);
virtual void act();
private:
int _damage;
char* _effect;
}
SwordImpl::SwordImpl(int d) {
_damage = d;
}
void SwordImpl::addEffect(const char* e) {
if (_effect)
{
delete _effect;
}
_effect = new char[strlen(e)+];
strcpy(_effect, e);
}
void SwordImpl::act() {
if (_effect)
{
cout << "Wielding sword with " << _effect << endl;
}
else
{
cout << "Wielding sword" << endl;
}
}
SwordImpl
同样的对于BowImpl来说,效果是在箭矢上的:
class BowImpl : public WeaponImpl {
public:
BowImpl(int);
virtual void addEffect(const char*);
virtual void act();
private:
int _damage;
char* _effect;
}
BowImpl::BowImpl(int d) {
_damage = d;
}
void BowImpl::addEffect(const char* e) {
if (_effect)
{
delete _effect;
}
_effect = new char[strlen(e)+];
strcpy(_effect, e);
}
void BowImpl::act() {
if (_effect)
{
cout << "Shoot an arrow with " << _effect << endl;
}
else
{
cout << "Shoot an arrow" << endl;
}
}
BowImpl
一个简单的UML图来说明上面的模式:
在这套框架下,无论我们是继续添加EpicWeapon,还是具体的DaggerImpl等,都非常方便。顺便说下,我们可以用Weapon提供的getWeaponImpl方法来获得想要的武器实现。具体代码在此就不作详解了(可以利用抽象工厂或者单例等对象创建型模式来做)。
Bridge模式有以下几个优点:
需要注意的是,如果我们仅有一个Implementor,可以没有必要创建一个抽象的Implementor类。这是Bridge模式的退化情况;在Abstraction与Implementor之间有一种一对一的关系。
森林里的中间有一只凶狠的魔法生物,混身散发着火焰,周围是烧焦的树木。它似乎被顽强的霍比特人激怒了,咆哮了起来。仅凭普通的武器,小霍比特人们无法战胜这只凶兽,他们中已有好几人身负重伤了。在千钧一发之刻,武器工坊发出耀眼的光芒,白雪公主从中拿出了一把镶满冰晶的奇弓,附着了寒冰的魔法。公主拉起弓弦瞄准了火兽,“真正的战斗现在才开始呢!” ……
今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!
手机扫一扫
移动阅读更方便
你可能感兴趣的文章