用反射解决水果篮问题 [Design, C#]

用反射解决水果篮问题 [Design, C#]

Solve The FruitBasket Problem With Reflection [Design, C#]

 

Written by Allen Lee

 

When will my reflection show who I am inside?—— Christina Aguielra,《Reflection》

 

1. 问题的引入

《OOD 启思录》[1]一书提到了一个有趣的“水果篮问题”[2],我把这个问题概要描述如下:

考虑这样一个水果篮,里面可以装有任意多的苹果(Apple)、桔子(Orange)和香蕉(Banana),而这些都派生自一个叫做水果(Fruit)的基类,并重写水果的 Print 和 Cost 两个方法以实现多态性。

Picture #01

有一天,你希望这些派生类更具个性,即各自具有一个别的派生类没有的特殊行为,例如苹果可以去核、桔子可以切片、香蕉可以剥皮。

Picture #02

并且,你希望迭代整个水果篮,让这些派生类执行它们特有的“个性行为”,即让苹果去核、让桔子切片、让香蕉剥皮,但你知道你已经不能再从多态性上获得更多的便利了,怎么办?

为了再次能从多态性上获得便利,你决定在水果类中加入一个叫 Prepare 的纯虚多态函数,并且让这些派生类把它们的“个性行为”实现到这个函数中。这样,当你使用多态性调用 Prepare 时,如果实际的类型是苹果,那么就去核;如果是桔子就切片;如果是香蕉就剥皮。

Picture #03

不过这个方法有个很大的弊端,就是假如你只希望迭代水果篮,让里面的部分派生类执行它们的“个性行为”,例如只需要水果篮里面的苹果去核,别的原封不动,那么这个方法就帮倒忙了!

对于这个问题,Arthur 在《OOD 启思录》中提到了两个流行的解决方案:“肥接口”方案和“记账”方案。

“肥接口”方案

该方案是作者认为目前最流行,也是很多设计者认为最好的方案。它让水果为苹果定义一个名叫 Core 的空虚方法,并让苹果重写该方法的实现,而其他的派生类就直接继承水果的空方法,然后用同样的方法来处理其他的派生类。这样,当你迭代整个水果篮,并且发送“去核”的消息时,苹果就会知道如何做,而其它派生类由于都直接继承水果类的 Core 这个空方法,就会呆在那里什么也不做。随后 Arthur 举了一个该方法不适用的情景,假如某个人增加了一个樱桃类,该类也需要去核,但用户只想迭代水果篮让苹果去核,那么这种方案也将帮倒忙。再者,如果派生类很多,水果类就要为这些派生类定义很多空方法,这样一来不好看,而来也会造成日后维护的负担。

Picture #04

“记账”方案

由于 Arthur 并没有提到该方案的名字,为了便于描述,我就为它起了这样一个名字。该方案提出水果篮除了维护一个水果列表,还应该为各个派生类维护一个单独的列表(当然,列表里面所存放的是指向实际对象的指针),这样就可以在无需改变水果层次结构的前提下根据用户的需要执行派生类的“个性行为”。然而,当派生类比较多的时候,簿记工作可能是一场噩梦,而且运行时的类型处理上也存在许多隐藏的问题。再者,如果某人增加了一个派生类,例如西瓜,那么你也将有可能需要为此向水果篮添加额外的代码。

Picture #05

Arthur 并没有在《OOD 启思录》中给出一个令人满意的解决方案,他认为这个问题“没有最好的解决方案,所有的方案都有需要解决的问题”。

 

2. 客户的要求

某日,我被告知要到会议室开一个紧急会议。当我来到会议室时,发现与会者中有一个魔鬼——一个很麻烦的客户,马上就有一种不祥的预感。客户要求开发一个水果篮的类,当我接过客户的需求描述后,我愣住了,我知道即将要面临“水果篮问题”!这个客户是公司的大客户,把工作推掉是不可能的;从过往的经验中,我知道该客户非常善变,如果我提供的产品没有足够的灵活性,那么将陷入一个可怕的维护噩梦。

会上,客户在白板上写下了一下这段 C# 代码:

//  Code #01

FruitBasket fb 
=   new  FruitBasket();
fb.Add(
new  Apple());
fb.Add(
new  Orange());
fb.Add(
new  Banana());
fb.Add(
new  Apple());
fb.Add(
new
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值