一个转换导出数据的代码,原先放在一个巨型函数内部,大致过程分为四段:从别的服务中提取数据、对数据进行一些转换操作、导出为另一格式、释放过程中的资源。具体的巨型函数的内容记不太清楚。
private List dataset; //save intermediate result
Export()
{
try
{
openService();
if(!sendRequest(callbackOnReceiveData))
{
return; //发送请求失败了。
}
processData();
exportToFile();
}
catch
{
blabla();
}
finally
{
a.close();b.close();c.close();
}
}
看到巨型函数,就考虑了重构,分解为四个方法。
Export()
{
try
{
retrieveData();
processData();
exportToFile();
}
finally
{
ReleaseResource();
}
}
在执行Extract Method的时候,引入了一个逻辑错误。
retrieveData()
{
openService();
if(!sendRequest(callbackOnReceiveData))
{
return; //发送请求失败了。
}
}
因为,这个方法失败返回的仅仅是retrieveData,后续依然会继续执行,而原来,是直接退出Export()终止整个过程的。重构引起程序的行为发生了变化,重构失败。虽然,后面的处理过程会检查dataset中是否有数据,以进行后续操作。
修改的办法是,将retrieveData加入返回标记,再进一步处理。
retrieveData()
{
openService();
return sendRequest(callbackOnReceiveData);
}
Export()
{
try
{
if(retrieveData())
{
processData();
exportToFile();
}
}
finally
{
ReleaseResource();
}
}
导致这个错误的一个原因,这三个方法具有调用顺序的耦合,这属于语义耦合。而代码本身没有显示出来。通过引入标记解决了这个问题,但是,潜在的引入了一个无关的概念——提取数据操作是否成功,某种程序上增加了复杂度。我觉得更理想的一种方案,是将dataset将级为局部变量,使用dataset本身来体现数据处理过程的选择顺序。
retrieveData()
{
openService();
if (sendRequest(callbackOnReceiveData))
return dataset;
return null;
}
Export()
{
try
{
var dataset = retrieveData();
var result = processData(dataset);
exportToFile(result);
}
finally
{
ReleaseResource();
}
}
通过参数传递的方式显示的表明了这三个方法之间的先后顺序。
阅读(461) | 评论(0) | 转发(0) |