21CTO学院导读:不重复自己(Do not Repeat Yourself)是软件开发之要义,它的目标就是避免代码重复。
不要重复自己,是软件开发的基本原则,它的目标是避免重复的代码发生。
每行代码在系统必须有单一,明确,权威的表现。
很多时间,你发现自己一遍又一遍编写同样的代码时,本文告诉你更实际好用的方法。
案例:不要重复自己
先看一个代码例子,来看它是否可以再改进,重构它以避免代码重复。
以下是一个Rport类,功能很简单,接收一些数据,通过控制台格式化打印一些信息。
代码如下:
class Report{
public function show(array $data){
echo "Report: " . ucwords(strtolower($data["name"])) . "/n";
echo "Product: " . ucwords(strtolower($data["product"])) . "/n";
echo "Start date: " . date("Y/m/d", $data["startDate"]) . "/n";
echo "End date: " . date("Y/m/d", $data["endDate"]) . "/n";
echo "Total: " . $data["total"] . "/n";
echo "Average x day: " . floor($data["total"] / 365) . "/n";
echo "Average x week: " . floor($data["total"] / 52) . "/n";
}
}
可以改进此代码吗?稍等一下,有报表的新需求,把内容保存到文件里。这很容易,我们对它进行一些复制粘贴,做一些小更改,几分钟就可能搞定。
代码如下:
class Report {
public function show(array $data) {
echo "Report: " . ucwords(strtolower($data["name"])) . "/n";
echo "Product: " . ucwords(strtolower($data["product"])) . "/n";
echo "Start date: " . date("Y/m/d", $data["startDate"]) . "/n";
echo "End date: " . date("Y/m/d", $data["endDate"]) . "/n";
echo "Total: " . $data["total"] . "/n";
echo "Average x day: " . floor($data["total"] / 365) . "/n";
echo "Average x week: " . floor($data["total"] / 52) . "/n";
echo "Average x month: " . floor($data["total"] / 12) . "/n";
}
public function saveToFile(array $data) {
$report = '';
$report .= "Report: " . ucwords(strtolower($data["name"])) . "/n";
$report .= "Product: " . ucwords(strtolower($data["product"])) . "/n";
$report .= "Start date: " . date("Y/m/d", $data["startDate"]) . "/n";
$report .= "End date: " . date("Y/m/d", $data["endDate"]) . "/n";
$report .= "Total: " . $data["total"] . "/n";
$report .= "Average x day: " . floor($data["total"] / 365) . "/n";
$report .= "Average x week: " . floor($data["total"] / 52) . "/n";
$report .= "Average x month: " . floor($data["total"] / 12) . "/n";
file_put_contents("./report.txt", $report);
}
}
等一下,我们真的完成了?当然,需求确实已经满足,但从技术角度,它似乎有些问题。可以看到很多重复项,虽然代码只有几行,至少写了两次写入文件。显然,这与DRY特性相反。
接下来,我们做一些重构。这两个方法的内容大致内容相同,唯一不同的是最终输出。我们来将主干内容提取到新方法中。这样,我们就有一个单一的功能,在一个地方创建报表,其它方法只负责如何处理即可。
class Report
{
public function show(array $data)
{
echo $this->createReport($data);
}
public function saveToFile(array $data)
{
file_put_contents("./report.txt", $this->createReport($data));
}
private function createReport(array $data): string
{
$report = '';
$report .= "Report: " . ucwords(strtolower($data["name"])) . "/n";
$report .= "Product: " . ucwords(strtolower($data["product"])) . "/n";
$report .= "Start date: " . date("Y/m/d", $data["startDate"]) . "/n";
$report .= "End date: " . date("Y/m/d", $data["endDate"]) . "/n";
$report .= "Total: " . $data["total"] . "/n";
$report .= "Average x day: " . floor($data["total"] / 365) . "/n";
$report .= "Average x week: " . floor($data["total"] / 52) . "/n";
$report .= "Average x month: " . floor($data["total"] / 12) . "/n";
return $report;
}
}
这样看起来好多了,还有一些小的重复项需要修复。
$report .= "Report: " . ucwords(strtolower($data["name"])) . "/n";
$report .= "Product: " . ucwords(strtolower($data["product"])) . "/n";
把这些转换提取到新方法,甚至可以提取到自己的单元测试库中。
private function normalizeName($name): string
{
return ucwords(strtolower($name));
}
另外还一个重复,日期格式:
$report .= "Start date: " . date("Y/m/d", $data["startDate"]) . "/n";
$report .= "End date: " . date("Y/m/d", $data["endDate"]) . "/n";
将它合并提取为:
private function formatDate($date): string
{
return date("Y/m/d", $date);
}
还有一个,计算平均数。
$report .= "Average x day: " . floor($data["total"] / 365) . "/n";
$report .= "Average x week: " . floor($data["total"] / 52) . "/n";
$report .= "Average x month: " . floor($data["total"] / 12) . "/n";
它们之间的计算结果并不完全一样,但是,我们可以合并成一个函数:
private function calculateAverage(array $data, $period): string
{
return floor($data["total"] / $period);
}
最后,重构的Report类是这个样子的:
class Report
{
public function show(array $data)
{
echo $this->createReport($data);
}
public function saveToFile(array $data)
{
file_put_contents("./report.txt", $this->createReport($data));
}
private function createReport(array $data)
{
$report = '';
$report .= "Report: " . $this->normalizeName($data["name"]) . "/n";
$report .= "Product: " . $this->normalizeName($data["product"]) . "/n";
$report .= "Start date: " . $this->formatDate($data["startDate"]) . "/n";
$report .= "End date: " . $this->formatDate($data["endDate"]) . "/n";
$report .= "Total: " . $data["total"] . "/n";
$report .= "Average x day: " . $this->calculateAverage($data, 365) . "/n";
$report .= "Average x week: " . $this->calculateAverage($data, 52) . "/n";
$report .= "Average x month: " . $this->calculateAverage($data, 12) . "/n";
return $report;
}
private function formatDate($date): string
{
return date("Y/m/d", $date);
}
private function calculateAverage(array $data, $period): string
{
return floor($data["total"] / $period);
}
private function normalizeName($name): string
{
return ucwords(strtolower($name));
}
}
DRY是编码过程中,开发基础结构以及部署的终结点,开发者应该努力做到这一点,从而全面改善代码环境,让其更具可读性和可管理性。
作者:正明
来源:21CTO学院
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/258246.html