转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。

原文出处:https://dzone.com/articles/dry-dont-repeat-yourself

我们之前就发过一篇相关的文章:https://www.cnblogs.com/powertoolsteam/p/12758496.html 其中也提到了包括DRY在内的一些软件开发的原则。

DRY 是软件开发的原则之一,其目的主要是为了避免代码重复,指导开发者尽量以抽象的思维去解决重复,基本上是,当您发现自己一遍又一遍地编写相同的代码时,可能会有更好的方法。

实际案例

让我们先看一个例子,看看这个例子是否可以改进,以及如何通过重构来避免代码重复。

这里有一个简单的Report类,该类接收一些数据并通过控制台以格式化的方式直接输出。

我们这里使用php的一个代码片段来举例,相信大家对代码的结构和想要完成的工作都不难理解,所以为了大家更容易理解,我只对一些下面用到的php函数定义做一个解释:

  1. echo()  函数输出一个或多个字符串
  2. ucwords()函数把字符串中每个单词的首字符转换为大写。
  3. strtolower() 函数把字符串转换为小写。
  4. file_put_contents() 函数把一个字符串写入文件中。
  5. floor() 函数向下舍入为最接近的整数。
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";
}
}

可以看到,上面的代码完成目标是没有任何问题的。

这时我们对Report类提出一个新的需求:把所有字符串也可以保存到文件中。

我们经过一通复制和粘贴上面的代码,新建一个名为saveToFile的函数,就可以很快的完成这个需求,代码如下:

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);
}
}

那么,上面的代码能够满足我们提出的需求吗?答案当然“是的”。但是从技术角度来看,这段代码似乎是有些问题的,它的重复代码到处都是。无论是对代码阅读及后期维护来讲,这都是一场噩梦。

所以我们需要进行一些重构,抽象能抽象的方法,让冗繁的代码变得更简洁。

首先,我们对Report类进行功能上的抽象,生成报告并输出一共可以分为两个功能,一个只负责创建Report,一个只负责如何处理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): 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和Products的名称函数使用重复:

$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原则,并随时抱着不要重复自己代码的想法去完成开发工作。

DRY原则的一个简单实践的更多相关文章

  1. 【阿菜做实践】利用go语言写一个简单的Pow样例

    本篇博客的主要内容是用go写一个简单的Proof-of-Work共识机制,不涉及到网络通信环节,只是一个本地的简单demo.开发IDE用的是JB Golang. 整个项目的文件结构如下: PoWdem ...

  2. DRY原则

    DRY--Don't Repeat Yourself Principle,直译为"不要重复自己"原则 DRY简而言之,就是不要写重复的代码.原则本身很简单,但是,对于OOAD(面向 ...

  3. 敏捷软件开发:原则、模式与实践——第14章 使用UML

    第14章 使用UML 在探索UML的细节之前,我们应该先讲讲何时以及为何使用它.UML的误用和滥用已经对软件项目造成了太多的危害. 14.1 为什么建模 建模就是为了弄清楚某些东西是否可行.当模型比要 ...

  4. 敏捷软件开发:原则、模式与实践——第10章 LSP:Liskov替换原则

    第10章 LSP:Liskov替换原则    Liskov替换原则:子类型(subtype)必须能够替换掉它们的基类型(base type). 10.1 违反LSP的情形 10.1.1 简单例子 对L ...

  5. 敏捷软件开发:原则、模式与实践——第8章 SRP:单一职责原则

    第8章 SRP:单一职责原则 一个类应该只有一个发生变化的原因. 8.1 定义职责 在SRP中我们把职责定义为变化的原因.如果你想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责.同时,我 ...

  6. 从一个简单的Java单例示例谈谈并发

    一个简单的单例示例 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这么写 public class UnsafeLazyInitiallization { private static Un ...

  7. Android 设计随便说说之简单实践(合理组合)

    上一篇(Android 设计随便说说之简单实践(模块划分))例举了应用商店设计来说明怎么做模块划分.模块划分主要依赖于第一是业务需求,具体是怎么样的业务.应用商店则包括两个业务,就是向用户展示appl ...

  8. 【转】DRY原则的误区

    很多编程的人,喜欢鼓吹各种各样的“原则”,比如KISS原则,DRY原则…… 总有人把这些所谓原则奉为教条或者秘方,以为兢兢业业地遵循这些,空喊几个口号,就可以写出好的代码.同时,他们对违反这些原则的人 ...

  9. 敏捷软件开发:原则、模式与实践——第13章 写给C#程序员的UML概述

    第13章 写给C#程序员的UML概述 UML包含3类主要的图示.静态图(static diagram)描述了类.对象.数据结构以及它们之间的关系,藉此表现出了软件元素间那些不变的逻辑结构.动态图(dy ...

随机推荐

  1. D:Sequence Swapping

    BaoBao has just found a strange sequence {<, >, <, >, , <, >} of length in his poc ...

  2. CF918C The Monster

    题目链接:http://codeforces.com/contest/918/problem/C 知识点: 贪心 解题思路: 枚举起点(当起点就是\(')'\)时直接跳过)并在此基础上遍历字符串,用一 ...

  3. 【Redis】Set常见应用场景

    微信抽奖小程序 >点击参与抽奖加入集合 SADD key {userID} >查看参与抽奖所有用户 SMEMBERS key >抽取count名中奖者 SRANGEMEMBER ke ...

  4. ShoneSharp语言(S#)的设计和使用介绍系列(6)— 字符串String

    ShoneSharp语言(S#)的设计和使用介绍 系列(6)— 字符串String 作者:Shone 声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ShoneS ...

  5. 实验二、OpenCV图像滤波

    一.题目描述 对下面的图片进行滤波和边缘提取操作,请详细地记录每一步操作的步骤. 滤波操作可以用来过滤噪声,常见噪声有椒盐噪声和高斯噪声,椒盐噪声可以理解为斑点,随机出现在图像中的黑点或白点:高斯噪声 ...

  6. SQL——表连接JOIN

    JOIN - 用于根据两个或多个表中的列之间的关系,从这些表中查询数据.    语法:SELECT columnName(s) FROM tableName1 JOIN tableName2 -- 查 ...

  7. EL表达式用法---查询博客

    jsp脚本:<%=request.getAttribute(name)%>EL表达式替代上面的脚本:${requestScope.name} 使用EL最主要的作用是获得四大域中的数据,格式 ...

  8. spring cloud系列教程第六篇-Eureka集群版

    spring cloud系列教程第六篇-Eureka集群版 本文主要内容: 本文来源:本文由凯哥Java(kaigejava)发布在博客园博客的.转载请注明 1:Eureka执行步骤理解 2:集群原理 ...

  9. 赛艇表演 51nod提高组模拟试题

    AC通道 题目描述 小明去某个地区观看赛艇比赛,这个地区共有n个城市和m条道路,每个城市都有赛艇比赛,在第i个 城市观看赛艇表演的价钱为ai, 去其他城市观看也需要支付赛艇表演的价格.任意两个城市之间 ...

  10. php CURL 发送请求封装

    cURL可以使用URL的语法模拟浏览器来传输数据,因为它是模拟浏览器,因此它同样支持多种协议,FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 ...