游戏逻辑思想
上QQ阅读APP看书,第一时间看更新

6. 代码修改与重构

在漫长的开发周期中,小C遇到了一些需求。当他准备开始撸相关的代码的时候,他发现隔壁的小B之前已经实现好了类似的功能,于是他开始纠结要自己写还是复制黏贴。思来想去,他决定先看看能不能拿过来用吧。

小C研究了下,总结出使用他人的代码有几种方式:

1. 别人写了一个class test,我们在自己的类内部对它进行组合。

2. 别人写了一个class test,我们继承它使用。

3. 别人有个函数,我想复制它的部分代码。

4. 别人有个函数,我想直接调用,但是参数可能比较多,也可能比较少。

那下面就要探讨这几种使用方式。首先假定我们需要使用到的功能大部分就是一个对象已经实现好的功能。那么这时候我们可以考虑选用前面2种方式。对于第2的方式,很直观的可以知道如果是继承,那么父类对子类的影响很大,因为父类可能会在自己的类中加入不确定的成员,另外如果我们按照这种思路,一个子类将继承自多个父类。多重继承带来的耦合性是非常大的,也是很多语言不支持的。那么如果我们使用方式1呢,耦合性会变低。在使用层面上,如果需要对外暴露引用到的对象的相关接口,会没有继承那么方便,有可能需要重新套上一层接口,也有可能需要对外暴露引用的类对象。

class Test{

public addRes();

public delRes();

}

class C extends Test{

}//外界可以直接调用addRes

class D{

private test:Test = new Test;

public addRes(){

return test.addRes(); //封装的方式

}

getTest(){

return test; //提供对象接口的方式

}

}

下面我们来重点讨论一下如果我们需要去使用别人函数的情况。如果我们只是需要里面的一小段代码,并且我们预估大概率用不到那个函数里面其他的功能,那么这个时候我们把那个目标函数复制一份,进行修改。如果我们无法做出预估,那么我们需要在那个函数上面进行改动。如果我们需要加参数,那么我们可以使用前面说过的tOption方案。如果我们需要的参数没那么多,比如原来的函数是5个参数的,且都不能为空。但是我们只用到2个参数,比如下面的情况:

function getPath(nStart, nEnd, nType1, nType2, nType3)

我们只需要nStart, nEnd,那么我们应该怎么办?小C不喜欢遇到这种情况,他想简单的给nType1, nType2, nType3赋值为0,然后去调用这个接口,我们假设项目里面大部分number的隐性默认值即为0。这个做法有点不舒服,于是小C进到那个函数里面看了一下,传0确实是ok的。但是这样一来又有点担心,万一以后加参数了怎么办?

这边其实有个更重要的思考,就是我们可以重构这部分代码,使其更合理。当我们的参数个数少于要调用的函数的参数个数时,我们要尝试把多余的参数剥离出来,让原来的函数调用我们新写的函数。最后期待的改造是这样子的:

function getPath(nStart, nEnd, nType1, nType2, nType3){

switch(nType1){

//

}

getPathInner(nStart, nEnd);

}

function getPathInner(nStart, nEnd){

}

遇到使用他人代码的时候,边重构边使用是更加有利于项目的一个方式,之前大部分程序的观点是尽量不要去改别人的代码,这种观点是值得商榷的,因为它可能会使得原来不舒服的接口造成更大的影响。我们在项目中秉持一个原则,如果有个接口让你不舒服,比如说多传了几个参数,那么我们一定要提出来,那一定是接口的设计不够简单或者没有提供更简单的接口形式。再深入讨论一下,如果你想要的接口的参数与他人提供的差距很大,这时候应该怎么办?

function addItem(nType, nCount, …)

function addItem(sUniqueId, nCount, …)

当出现这种情况时,我们可以允许存在2种类似的接口,但是,我们一定需要把它们的共同部分抽象出来,作为一个单独的函数。不断抽象也是在使用他人函数的时候要做的一个重构,也能使项目变的更好。

最后,简单总结下。使用他人的代码不是简单的复制黏贴,如果他人写的够好,那么就直接引用。如果是写的不够好,那么我们就去重构。只有互相重构,才能使得整体代码效率变高。