面向对象的多态(三)

这篇文章跟多态无关了,只是对前面的一点扩充。

上一篇文章说到,为了把A函数作为参数传递给B函数,我们把A函数封装到了C类里面,而让B函数直接接收C类。那么,有没有办法不通过类,直接让一个函数直接接收另一个函数呢?先给一个定义,能接收函数作为输入的函数,称之为高阶函数。现在越来越多的语言开始支持这个特性了(或者说是我孤陋寡闻,现在才知道很多语言支持高阶函数)。

那么我们试试用高阶函数来重新实现我们的test script。

我们一直用来做demo的语言,C#,支持一种叫委托(delegate)的类型,委托是一种定义方法签名的类型,可以与具有兼容签名的任何方法关联。 可以通过委托调用方法。委托用于将方法作为参数传递给其他方法。听起来有点晕,我们还是看代码吧。

delegate void Behavior();

上面代码我们定义了一个叫Behavior的委托,它可以指向任何不接收任何参数,返回值为void的函数/方法。回想一下,我们的NavigateInXxx()和BackInXxx()是符合条件的。然后我们往上看几行,“委托用于将方法作为参数传递给其他方法”,这不正是我们想做的事情么?

经过上一次的重构,我们知道,把switch case移除以后,Navigate和Back函数是没必要存在的,所以,这次我直接从test script开始改写。

void SimpleTestCase(Behavior navigate, Behavior back)
{
    navigate();   
    ValidateNavigate(); 
    back();
    ValidateBack(); 
}

SimpleTestCase(NavigateInIE, BackInIE);     //在IE下测试
SimpleTestCase(NavigateInFF, BackInFF);     //在Firefox下测试

我们看看变化最大的是什么?SimpleTestCase的参数类型变成了Behavior,一个委托类型,正是这一变化,让我们可以把NavigateInXxx和BackInXxx函数作为参数传递给了SimpleTestCase。

那它解决了添加Chrome测试的问题了吗?老样子,我们列出为了支持Chrome要做的事情:

1、增加(录制)NavigateInChrome方法

2、增加(录制)BackInChrome方法

3、增加test script的调用,SimpleTestCase(NavigateInChrome, BackInChrome)

仍然很好地实现了对扩展开放,对修改关闭。

可能一个直接的问题来了,两种方法都能实现,哪种更好呢? 这个问题似乎没有标准答案。高阶函数从函数的粒度对重用进行支持,使用起来更加灵活,例如SimpleTestCase,不考虑实用性的话,我完全可以把NavigateInIE和BackInFF作为一组参数传递过去。而面向对象的实现方法就做不到了,但这似乎恰恰是面向对象的初衷所在,定义好一组契约(每个Browser都应该提供一组Navigate和Back方法)让双方遵守,防止出现NavigateInIE和BackInFF这种非法组合。所以问题的关键,在于你重构代码是为了应对什么样的变化。

就SimpleTestCase的例子而言,以面向对象的方式重构更为适合,因为所有的浏览器,包括将来会增加进来的,都应该实现共同的契约(实现必须的方法,如果有浏览器对象没实现Navigate方法,test case就跑不过去了)。

而考虑一个更简单的案例,从1-10,我们要做各种类型的计算(加1,减1,平方...),这时候用高阶函数会更灵活。 反过来如果是面向对象的解决办法,那么我们会定义一堆诸如AddOne, SubOne的类,这些类都只有一个Calculate(int i)的方法,从语义表达上看,显得很累赘,不如定义一堆AddOne(int i), SubOne(int i)函数来得清晰。

原文地址:https://www.cnblogs.com/woodylic/p/2559122.html