满足 Martin 的简洁代码功能标准的程序结构
Program Structure to meet Martin's Clean Code Function Criteria
我一直在阅读 Clean Code by Robert C. Martin 并且有一个关于函数和程序结构的基本(但基本)问题。
书中强调函数应该:
- 要简短(如 10 行或更少)
- 只做一件事
我不太清楚如何在实践中应用它。例如,我正在开发一个程序来:
- 加载基线文本文件
- 解析基线文本文件
- 加载测试文本文件
- 解析测试文本文件
- 将解析的测试与解析的基线进行比较
- 汇总结果
我尝试了两种方法,但似乎都不符合 Martin 的标准:
方法 1
设置一个 Main 函数来集中控制工作流中的其他函数。但是 main()
最终可能会变得很长(违反#1),并且显然会做很多事情(违反#2)。像这样:
main()
{
// manage steps, one at a time, from start to finish
baseFile = loadFile("baseline.txt");
parsedBaseline = parseFile(baseFile);
testFile = loadFile("test.txt");
parsedTest = parseFile(testFile);
comparisonResults = compareFiles(parsedBaseline, parsedTest);
aggregateResults(comparisonResults);
}
方法 2
使用 Main 触发函数 "cascade"。但是每个函数都在调用一个依赖项,所以它们似乎仍然在做不止一件事(违反#2?)。例如,内部调用聚合函数需要进行结果比较。流程似乎也是倒退的,因为它从最终目标开始,并在进行过程中调用依赖项。像这样:
main()
{
// trigger end result, and let functions internally manage
aggregateResults("baseline.txt", "comparison.txt");
}
aggregateResults(baseFile, testFile)
{
comparisonResults = compareFiles(baseFile, testFile);
// aggregate results here
return theAggregatedResult;
}
compareFiles(baseFile, testFile)
{
parsedBase = parseFile(baseFile);
parsedTest = parseFile(testFile);
// compare parsed files here
return theFileComparison;
}
parseFile(filename)
{
loadFile(filename);
// parse the file here
return theParsedFile;
}
loadFile(filename)
{
//load the file here
return theLoadedFile;
}
显然函数需要相互调用。那么,构建满足 Martin 标准的程序的正确方法是什么?
我认为您没有考虑上下文,对规则 2 的解释是错误的。 main()
函数只做一件事,那就是 一切 ,即 运行 整个程序。假设你有一个 convert_abc_file_xyz_file(source_filename, target_filename)
那么这个函数应该只做它的名字(和参数)所暗示的一件事:将格式 abc 的文件转换为格式 xyz。当然,在较低的层次上,要实现这一点还有很多事情要做。例如读取源文件(read_abc_file(…)
),将数据从格式abc转换为格式xyz(convert_abc_to_xyz(…)
),然后将转换后的数据写入新文件(write_xyz_file(…)
)。
第二种方法是错误的,因为编写只做一件事的函数变得不可能,因为每个函数都在“级联”调用中做所有其他事情。在第一种方法中,可以测试或重用单个函数,即只需调用 read_abc_file()
来读取文件。如果该函数调用 convert_abc_to_xyz()
,而后者又调用 write_xyz_file()
,那就不可能了。
我一直在阅读 Clean Code by Robert C. Martin 并且有一个关于函数和程序结构的基本(但基本)问题。
书中强调函数应该:
- 要简短(如 10 行或更少)
- 只做一件事
我不太清楚如何在实践中应用它。例如,我正在开发一个程序来:
- 加载基线文本文件
- 解析基线文本文件
- 加载测试文本文件
- 解析测试文本文件
- 将解析的测试与解析的基线进行比较
- 汇总结果
我尝试了两种方法,但似乎都不符合 Martin 的标准:
方法 1
设置一个 Main 函数来集中控制工作流中的其他函数。但是 main()
最终可能会变得很长(违反#1),并且显然会做很多事情(违反#2)。像这样:
main()
{
// manage steps, one at a time, from start to finish
baseFile = loadFile("baseline.txt");
parsedBaseline = parseFile(baseFile);
testFile = loadFile("test.txt");
parsedTest = parseFile(testFile);
comparisonResults = compareFiles(parsedBaseline, parsedTest);
aggregateResults(comparisonResults);
}
方法 2
使用 Main 触发函数 "cascade"。但是每个函数都在调用一个依赖项,所以它们似乎仍然在做不止一件事(违反#2?)。例如,内部调用聚合函数需要进行结果比较。流程似乎也是倒退的,因为它从最终目标开始,并在进行过程中调用依赖项。像这样:
main()
{
// trigger end result, and let functions internally manage
aggregateResults("baseline.txt", "comparison.txt");
}
aggregateResults(baseFile, testFile)
{
comparisonResults = compareFiles(baseFile, testFile);
// aggregate results here
return theAggregatedResult;
}
compareFiles(baseFile, testFile)
{
parsedBase = parseFile(baseFile);
parsedTest = parseFile(testFile);
// compare parsed files here
return theFileComparison;
}
parseFile(filename)
{
loadFile(filename);
// parse the file here
return theParsedFile;
}
loadFile(filename)
{
//load the file here
return theLoadedFile;
}
显然函数需要相互调用。那么,构建满足 Martin 标准的程序的正确方法是什么?
我认为您没有考虑上下文,对规则 2 的解释是错误的。 main()
函数只做一件事,那就是 一切 ,即 运行 整个程序。假设你有一个 convert_abc_file_xyz_file(source_filename, target_filename)
那么这个函数应该只做它的名字(和参数)所暗示的一件事:将格式 abc 的文件转换为格式 xyz。当然,在较低的层次上,要实现这一点还有很多事情要做。例如读取源文件(read_abc_file(…)
),将数据从格式abc转换为格式xyz(convert_abc_to_xyz(…)
),然后将转换后的数据写入新文件(write_xyz_file(…)
)。
第二种方法是错误的,因为编写只做一件事的函数变得不可能,因为每个函数都在“级联”调用中做所有其他事情。在第一种方法中,可以测试或重用单个函数,即只需调用 read_abc_file()
来读取文件。如果该函数调用 convert_abc_to_xyz()
,而后者又调用 write_xyz_file()
,那就不可能了。