最近在读一本很棒的关于软件设计模式的书《HeadFirst设计模式》,这本书很有趣,用幽默的语言和图片介绍了许多设计模式,现在我刚看完第一个设计模式,来总结一下加深记忆与理解。
问题提出
假设你有一个鸭子的基类Duck,类中定义了一些鸭子的共性,并且有许多派生类,如绿头鸭,白鸭,橡皮鸭,打猎用诱饵鸭(一种木头鸭子)等等。这时客户要求实现一些鸭子的飞行方法与发出叫声的方法,你该怎么做?
1、使用继承?
将飞行方法与发出声音的方法声明为虚方法,由子类继承后再各自实现。这将会导致一个问题,橡皮鸭与打猎用诱饵鸭不会飞,打猎用诱饵鸭不会叫,他们不该有飞行或者发出声音的方法,使用继承可能会带来许多问题。
2、使用接口?
因为某些鸭子飞行或发出声音的方式不同,因此用基类来实现接口时不合适的。所以我们要为每个子类实现这些接口,假如Duck类由40几个派生类,那就得为这所有的派生类实现接口。如果一些鸭子飞行方式或发出得声音相同,就会出现大量的代码重复,为以后代码的维护与更改带来巨大的麻烦。
设计原则
1、找出代码可能会经常发生变动的部分,将其独立出来,这样改动这一部分时便不会影响到其他部分。
2、针对接口编程,而不是针对实现编程,(在这里,我们将不再使用基类Duck或其派生类来实现方法或接口,而是定义一个接口,创建新的类来实现他,这些类称为行为类,等下我们就可以看到这种设计的灵活性)。
解决方案
我们将定义两个接口,分别用于表示飞行与发出声音,然后根据鸭子们不同的飞行方式与发出的声音,设计不同的行为类,然后在鸭子类中定义两个函数,利用接口可以引用实现该接口的类的原理,为不同的鸭子设定飞行模式与发出的声音。这样的设计有继承与接口的优点,同时在一定程度上消除了两者的不足。在鸭子这个例子这样设计的优点可能不明显,但是但你碰到需要根据不同情况设定对象行为的情况时,这中设计就会非常有用。
Duck类:
abstract class Duck
{
protected IFly m_flyBehavior; //Fly接口的变量
protected IMakeSound m_makeSoundBehavior; //MakeSound接口的变量
//上面两个接口很关键,就是因为他们才实现了行为的任意切换
public Duck() { }
public abstract void Display();
public void PerformFly() //输出飞行内容
{
m_flyBehavior.Fly();
}
public void PerformMakeSound() //输出发出的声音的内容
{
m_makeSoundBehavior.MakeSound();
}
public void Swim()
{
Console.WriteLine("I am swimming");
}
public void SetFlyBehavior(IFly fly) //设定飞行模式
{
this.m_flyBehavior = fly;
}
public void SetMakeSoundBehavior(IMakeSound makeSound) //设定发出的声音
{
this.m_makeSoundBehavior = makeSound;
}
两个接口:
//飞行接口
interface IFly
{
void Fly();
}
//发出声音的接口
interface IMakeSound
{
void MakeSound();
}
行为类:
//用翅膀飞行的鸭子
class FlyWithWings:IFly
{
void IFly.Fly()
{
Console.WriteLine("I am flying with wings");
}
}
//不会飞的鸭子
class NotFly:IFly
{
void IFly.Fly()
{
Console.WriteLine("I can not fly");
}
}
//给鸭子装火箭(笑。。)
class FlyWithRocket:IFly
{
void IFly.Fly()
{
Console.WriteLine("I am flying with a rocket");
}
}
//橡皮鸭的声音
class RubberDuckSound:IMakeSound
{
void IMakeSound.MakeSound()
{
Console.WriteLine("吱吱吱");
}
}
//木头鸭的声音
class WoodDuckSound:IMakeSound
{
void IMakeSound.MakeSound()
{
Console.WriteLine("I can not make sound");
}
}
//真鸭子的声音
class RealDuckSounds:IMakeSound
{
void IMakeSound.MakeSound()
{
Console.WriteLine("嘎嘎嘎");
}
}
//开心的鸭子的声音(我编的)
class HappyDuck:IMakeSound
{
void IMakeSound.MakeSound()
{
Console.WriteLine("哈哈哈");
}
}
测试:
ModelDuck modelDuck = new ModelDuck(); //创建一只橡皮鸭
modelDuck.SetFlyBehavior(new NotFly()); //设定飞行模式为不会飞
modelDuck.SetMakeSoundBehavior(new RubberDuckSound()); //设定声音为吱吱吱
modelDuck.PerformFly();
modelDuck.PerformMakeSound();
modelDuck.SetFlyBehavior(new FlyWithRocket()); //给橡皮鸭装火箭
modelDuck.SetMakeSoundBehavior(new HappyDuck()); //鸭子开心了
modelDuck.PerformFly();
modelDuck.PerformMakeSound();
结果如下:
。