乱砍设计模式之一: 源代码下载 class IDCart {}; //身份证 class Person { public: ….. private: string name; int age; IDCart idcart; };(看到这个定义,基本可以确定该套系统是为中国公民做的。身份证对于身处外地打工的人来说是重要的。前段时间一哥们把身份证弄丢了,由于跳槽的缘故,他离开了原来所在的城市。人民警察要他把户口迁移出去,大城市的户口一般工作单位都不给办。迁哪儿去?想想诺大的中国那里是我们的容身之所?派出所百般刁难,不给补办。好不容易弄了一个临时身份证,拿着去银行注销银行卡,银行居然也不给办。哥们郁闷至极,比钱包丢的时候都郁闷。说是要户籍改革,不知道会改些什么?想想一年前,我被小偷顺走了钱包,那时候还是一代身份证,感觉办起来比现在方便了很多。户籍是要改革了吗?会改成什么样呢?) 看我们的定义,一个人拥有名字,年龄,身份证等属性。由于身份证有一些相关的操作:发放,挂失,补办等操作,我们把它提炼成一个单独的类。此处我们使用聚合的方式来完成对于身份证的处理,所有对于身份证的操作,都通过idcart来实现。如:发放身份证的操作,在聚合条件下就变成了: class IDCart { public: void PutOut(){} }; class Person { public: void PutOutIDCart() { idcart->PutOut(); } void SetIDCart(IDCart cart) { idcart = cart; } private: string name; int age; IDCart idcart; };聚合说白了就是在一个类中定义一个另一个类的对象,然后通过该被聚合的对象来实现相应本需要聚合类实现的功能。 使用聚合的优点是:可以帮助我们更好的封装对象,使每个类集中在单一的功能上,使类的继承层次也不会无限制的增加,避免出现类数量的膨胀。而且使用聚合还有一个优点就是可以动态的改变对象(下面会讨论到)。不过聚合相对于继承来说,没有继承直观,理解比较困难。 在确定使用继承还是聚合的时候,有一个原则:继承体现的类之间“是一个”的关系。例如我们需要对学生,工人进行单独的处理。那么我们的例子应该是这样: Class student : public person { }; Class worker : public person { }; 也就是说学生是一个人,而工人也是一个人。学生和人之间体现的是“是一个”的关系。而工人也一样。 而身份证对于人来说,是人的一个属性。那么我们就可以提炼出来成为一个单一的类,通过聚合来实现。 接着还是回到我们策略模式的例子,同样在我们的例子程序中,可以把武器提炼成一个单独的类,类图如下: 我们提炼出一个Weapon类,将在General中使用。噫!怎么又有一个m_strWeapon?你可能要开骂了:谁他妈是傻子呢?这样做不又回到了过程化设计的鬼样了?别急,提供这个错误的方法,只是为了给你提供另一个面向对象的设计原则:尽量针对接口编程,而不要针对实现编程。 C++中没有象C#或者Java等面向对象语言那样,提供对Interface的语言支持。但接口也不过是一个概念,我们使用纯虚函数类,等同于接口。我们提供一个不被实例化的基类事实上也可以当作接口来用。针对接口编程的意义是:可以不用知道对象的具体类型和实例,这样可以减少实现上的依赖性。可以帮助我们提高程序的灵活性。好了,我们再重新来设计类图: 新的类图中Weapon被抽象成了一个接口,拥有一个虚函数Assault,拥有两个子类Lance和Sword。而在General类中,我们拥有了一个新的成员:m_pWeapon。而攻击的函数变成了performAssault,它是通过调用m_pWeapon->Assault()来实现攻击的。这样一来武器就可以随时变更了。我们来看看简单的代码实现: //武器类 class Weapon { public: virtual void Assault() = 0; //纯虚函数 }; //长枪类 class Lance : public Weapon { public: virtual void Assault() { cout << " I kill enemy with the Lance and can kill 10 every time!" << endl; } }; //宝剑类 class Sword : public Weapon { public: virtual void Assault() { cout << " I kill enemy with the sword and can kill 20 every time!" << endl; } }; //武将类 class General { private: string m_strName; Weapon *m_pWeapon; public: //构造函数,初始化m_pWeapon为Lance类型 General(string strName):m_strName(strName),m_pWeapon(new Lance()) { } //指针是需要删除的 ~General() { if ( m_pWeapon != NULL ) delete m_pWeapon; } //设置武器类型 void SetWeapon(Weapon *pWeapon) { if ( m_pWeapon != NULL ) delete m_pWeapon; m_pWeapon = pWeapon; } void performAssault() { m_pWeapon->Assault(); } void Advance() { cout << "Go,Go,Go!!!" << endl; } }; int main(int argc, char* argv[]) { //生成赵云对象 General zy("Zhao Yun"); //前进 zy.Advance(); //攻击 zy.performAssault(); //更换武器 zy.SetWeapon(new Sword()); zy.Advance(); zy.performAssault(); return 0; }其实程序的实现相当简单,就是一个简单的聚合加应用针对接口编程的例子。这就是我们要讲的第一个模式:策略模式。重新看一下它的定义:定义一系列的算法,把它们一个个的封装起来,并且使它们可以相互转换。这里所说的一系列的算法封装就是通过继承把各自的实现过程封装到子类中去(我们的例子中是指Lance和Sword的实现),而所说的相互转换就是我们通过设置基类指针而只向不同的子类(我们的例子上是通过SetWeapon来实现的)。 是不是很简单呢?如果你懂虚函数的话,千万别告诉我没看懂,这是对我无情的打击,也许导致我直接怀疑你的智商。如果你不懂虚函数,那回头找本C++的书看看吧,推荐的是《C++ primer》,第四版出了。 参考书目
|
|
来自: kyo_siye > 《乱砍设计模式系列》