转载:设计模式之接口理解

假设你所在的公司开发了一套鸭子模拟游戏,它可以模拟各种不同的鸭子,在水上游泳,同时还能发出“嘎嘎”的叫声,相当真实,因此卖的很不错。这个游戏是用标准的OO技术来设计的,一个抽象的Duck基类,有发出“嘎嘎”叫声的Quack方法和在水里游泳的Swim方法,同时它还有一个抽象的Display方法,每一个Duck子类(如MallardDuckRedheadDuck)都将之重写,以便实现自己与众不同的外观。如下图:

 

你们公司有很多竞争对手,他们可不是吃素的,在日益增大的市场压力下,你们老板做出了一个决定,要改进这个游戏,让游戏里的鸭子飞起来,成功的话,一定可以打败所有的人。哦,这个艰巨的任务就交给你了,你是名很好的OO程序员,不是吗?

 

接到任务,你马上就开始了。这还不容易?在Duck基类里加一个Fly方法,这样所有的Duck子类都可以获得这个方法,所有的鸭子都可以飞了。太简单了,这就是OO的威力呀。

第二天,你高高兴兴地到公司上班,昨天已经把改好的程序交给老板了,他看过之后肯定会说,这小子,做事情做的真麻利。你还在想象着老板会怎样夸奖自己的时候,电话响了,老板打来的:“我现在在董事会上,刚把你的程序做了一个演示,鸭子能飞了,但是我怎么看见一只橡皮鸭也在飞?玩笑开大了吧?小心点,不要让我炒了你!”真吓人,稍稍平静了一下受惊的心情,仔细想想,确实是自己考虑的不够周到。并不是所有的鸭子都可以飞,可是把Fly方法放在基类里,那所有种类的鸭子都能飞了呀。怎么办?

 

你灵机一动,又想了一个方法,在橡皮鸭的子类中,把Fly方法重写掉,让它什么都不做!一阵高兴过后,你又想了“假如以后我又要加一个木制的诱饵鸭的时候,我该怎么办,它们不能叫也不能飞。”是呀,怎么办?又把FlyQuack方法重写掉?一两个还好,假如有几十个,上百个,一个一个去重写?太可怕了,刚刚变好的心情一下子又变的糟糕起来。

 

你认识到,把FlyQuack方法放在基类中,然后通过继承来达到老板想要的效果的方法是不行的。你需要一个清晰明了的解决方案。这时,你猛地拍了一下脑袋,用接口怎么样?

 

使用接口

 

Fly方法从Duck基类中提取出来,放在一个叫IFlyable的接口中,同样地,把Quack方法也提取出来,放到IQuackable接口中。这样只有那些需要飞行动作的Duck子类才去实现IFlyable接口,需要能发出叫声的Duck子类才去实现IQuackable接口。比如RubberDuck不能飞,所以不实现IFlyable接口,但它可以发出吱吱声,所以要实现IQuackable接口,又如DecoyDuck不能飞也不能叫,所以它只用从Duck基类继承就可以了。UML图如下:

 

这个方法似乎把问题解决了,但是再仔细想想,确实是这样吗?

 

由于C#的接口中只允许声明成员签名,而不允许有任何代码实现,所以每一个实现IFlyable接口的子类都需要提供自己的Fly方法实现,这样就造成了大量的重复代码。试想一下,假如程序里一共有48个实现了IFlyable接口的子类,实然有一天老板说“我们需要改变一下Fly方法的实现(比如需要鸭子能飞的更高一些)”,那你该怎么办?一个一个地把那48个子类全部改一遍?天啊,这是不可想象的。这个方法只能说把你从一个噩梦带到了另一个噩梦,并没有解决问题。

 

事实是这样的,用户的需求每天都在变(天知道他们想干嘛?),假如我们能够尽可能的减少因用户需求改变而造成的程序改动,那我们的活儿要轻松的多,我们可以把更多的时间放在我们自己感兴趣的事上(踢踢球,打打游戏),而不是下班后还在老板的眼皮下加班加班再加班。

 

应该怎么办呢?这里会提到一个准则,它几乎是所有设计模式的精髓,那就是:

 

把变化的部分提取出来,将之封装

 

简而言之,就是把预期会发生变化的代码分离出来,单独放在一块儿,以后我们就可以很容易地修改它,而不会影响到已有的代码。

 

我们看看,在这里的这种情况下,我们应该怎么做?

 

对接口而非对实现编程

 

先分析一下造成麻烦的原因,老板需要程序做一些改动,在这里,就是两个方法,FlyQuack方法。这两个方法需要有很多种不同的实现,而且以后还有可能会增加、修改、删除,那么按照上面的原则,我们就应该把这部分会变动的代码提取出来,将之与Duck基类分离。这样,以后这部分代码的改动就不会影响到Duck类。清楚了问题所在,那就快点动手解决吧!

 

等等,还需要了解一个东西——多态。

 

简要说一下多态。

 

什么叫多态?多态就是指为同名的方法提供不同的实现的能力,它使得我们不用关心方法的具体实现而仅仅依靠其名称来进行调用操作。

 

通过多态我们能干嘛?多态的用处相当大!概括地说,通过它,我们可以使代码更加清晰、简练,具体可以看看Allen的文章《今天你多态了吗?

 

好了,你是多么好的一名OO程序员,多态应该是了如指掌了。我们来看看怎么用多态来解决这个问题。

 

1.         把变化的部分提取出来:提取Fly方法,把它放到IFlyBehavior接口中,不同的Fly行为类(子类型)都实现这个接口,比如FlyWithWingsFlyNoWay等。同样地,提取Quack方法,放到IQuackBehavior接口中,不同的Quack行为类都实现这个接口,如QuacksSqueakMuteQuack等。如下图:

 

1.         因为已将FlyQuack提取出去了,所以把FlyQuack方法从Duck基类中删除。

2.         Duck基类中加入两个成员变量,分别声明为上面的那两个接口类型(注意,是接口类型,而非特定的子类型)。Duck子类可以在实例化时设置这两个变量,也就是设置鸭子的行为。

 

如果想在运行时动态的设置Duck子类的行为,还可以加入两个Set方法,SetFlyBehaviorSetQuackBehavior,在里面改变flybehaviorquackbehavior变量的值。这样,就可以让一个诱饵鸭摇身一变,变成红头鸭,嘎嘎的飞上天了。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值