发布于 2026-01-05 4 阅读
0

必知:编程基本工程原则 1. 不要重复自己(DRY 原则) 2. 迪米特法则(LoD 原则) 3. KISS 原则(保持简单,笨蛋) 4. YAGNI 原则(你不需要它) 5. SoC 原则(关注点分离) 6. 童子军法则(重构) 7. TDA 原则(告知,不要询问) 8. P^3 原则(P 立方原则)

必知:编程基础工程原理

1. 不要重复自己(DRY 原则)

2. 德墨忒尔法则(LoD)

3. KISS(保持简单,笨蛋)

4. YAGNI(你不需要它)

5. SoC(关注点分离)

6. 童子军法则(重构)

7. TDA(告知而非询问)

8. P³(P立方原理)

大家好!这篇文章是我在OhMyScript上发表的原文的重写版本,内容涵盖了所有基本的工程编程原则,旨在帮助开发者成为更优秀的开发者,或者编写和维护简洁的代码。

替代文字

我们必须时刻提醒自己,我们编写的代码最终会被其他人/开发者使用。因此,请不要给他人增加负担。编写易于理解、简洁明了的代码至关重要,这样既不会让人抓狂,也不会让其他人难以处理。

大多数程序员和开发者都渴望不断提升自己,学习并掌握新的技术栈、工具或其他相关知识。但在编程或解决问题时,我们常常忽略一些基本规范。

你认为优秀的程序员应该具备哪些素质?

如果你问 10 位开发者同样的问题,你肯定会得到 10 个不同的答案。虽然答案的措辞各不相同,但它们很可能表达的是同一个意思。作为一名职业开发者,一年来我学到了很多东西,如果本科期间就能掌握这些知识,在维护大型代码库时会非常有帮助。

PS:我本科期间做的项目都很糟糕,完全违背了我在这里解释的所有原则。

以我个人的经验和遇到的问题为例,我认为优秀的程序员不仅要能理解具体问题,还要能提出最可行的解决方案,不仅要着眼于眼前,更要着眼于长远。除了不断学习新技术之外,我认为所有开发者都应该遵循以下一些基本原则:

1. 不要重复自己(DRY 原则)

顾名思义,“不要重复自己”原则(又称 DRY 原则)只是建议我们不要在整个项目或代码库中重复编写代码。

编写代码时,务必避免代码重复。这条原则简单来说就是“一次编写,两次使用”。

替代文字

从长远来看,重复的代码将难以管理和维护,因为会不断出现新的需求。

下面给出一个简单的例子来说明这一点,其中,如果巧克力少于 5 块,那么使用非 DRY 方法至少是可以想象的。随着巧克力的数量/规模增加,使用非 DRY 方法管理这样的代码将变得非常困难。

let costofChocolate = [10,12,15,20];

/**
** Non - DRY Approach
** Suppose you need to add ₹ 2 as tax for each
**/

costofChocolates[0] = costofChocolate[0] + 2;
costofChocolates[1] = costofChocolate[0] + 2;
costofChocolates[2] = costofChocolate[0] + 2;
costofChocolates[3] = costofChocolate[0] + 2;

/**
** DRY Approach
** Suppose you need to add ₹ 2 as tax for each
**/

function addTax(chocolatesCost,taxAmount) {
   for(let i =0; i<chocolatesCost.length;i++){
      chocolatesCost[i]=chocolatesCost[i]+taxAmount;
   }
  return chocolatesCost
}

addTax(costofChocolate, 2);
Enter fullscreen mode Exit fullscreen mode

除了避免重复代码之外,这还能提高代码的可读性,并允许在项目的任何其他组件/部分中复用特定功能。DRY 原则最大的优点在于可维护性。如果确实存在需要修复的错误,只需在一个地方进行修补,而无需在多个地方进行修补。

笔记:

  1. 有时候,我们需要非常谨慎地遵循 DRY 原则。因为有时,两段代码看起来可能很相似,但实际上却存在非常细微的差别。
  2. 避免过早进行DRY优化。

2. 德墨忒尔法则(LoD)

德米特法则是一种设计原则,也称为最小知识原则。该法则最初指出:

对于所有类 C 以及附加到 C 的所有方法 M,M 向其发送消息的所有对象都必须是

M 的论证对象,包括自身对象或

C语言的实例变量对象

(由 M 创建的对象,或由 M 调用的函数或方法创建的对象,以及全局变量中的对象,都被视为 M 的参数。)

最初,当 Simula 进入市场时,它是第一种具有面向对象原则特征的语言;对象只是被用作将数据从一个方法传递到另一个方法的媒介。

“对象”的基本理念是彼此之间传输数据,也就是说,它们之间可以进行通信。如果你阅读原始定律,它简单地蕴含了以下几点:

  • 对象应该只与其直接相邻的对象进行交互(相邻对象 -> 方法或数据)。
  • 物体不应该依赖于其他邻居。
  • 对象应该只公开其他实体使用的信息。

让我解释一下这个简单的例子;

/**
** Simple Example of Law of Demeter in JavaScript
** 
** Assume an object userObj of the class User
** 
**/
const userObj = new User(); 

userObj.getUsers().filterAge();  // Breaches the Law of Demeter

let userList = userObj.getUsers()  // Breaches the Law of Demeter
let filterUsers = userObj.filterAge(); // Does not breach the Law of Demeter

/*
** Even while structuring /  formatting the data
** 
** User's designation is to be accessed from the variable
*/

user.designation._id // Breaches
user.designation.designationName // Breaches

user.designationId // Does not breach 
user.designationName // Does not breach
Enter fullscreen mode Exit fullscreen mode

这条法律确保了系统采用解耦式系统设计。

3. KISS(保持简单,笨蛋)

我坚信,KISS 作为“保持简单和明智”的缩写更有意义。

替代文字

“保持简单,笨蛋”真是一条绝妙的生活秘诀!
正如这句话所说:

“凡事都应该尽可能简单,而不是过于简单。”

  • 阿尔伯特·爱因斯坦

作为程序员,你编写的代码或创建的设计都应该力求简洁。它应该尽可能地简单明了。
有时我们会遇到复杂的问题描述或需求。大多数情况下,解决方案其实很简单,只是我们不知道该如何处理。

在开始解决问题之前,先理解问题陈述。很多时候,解决方案是现成的,但我们却忽略了规划如何编写解决方案;而一旦找到了解决方案,我们又很少会去检查这是否是最佳、最优的解决方法。

最简单的例子,也是我们刚开始做开发者时总是做不到的。

/**
** Simple Example of Short Circuit Evaluation in JavaScript
** 
** This is first thing we learn in C, C++ or Java when we learn 
** expressions & operators, yet fail to apply this.
** 
**
** Assuming you want to console a variable; only if the variable username  
** is defined and not null  
** 
**/

// Breaching the KISS
if(username == undefined || username == null || username == ''){
          console.log('Error');
}
else {
     console.log(username);
}


//Does not reach the KISS Principle
console.log( username || 'Error' );  
Enter fullscreen mode Exit fullscreen mode

即使是 Node 的异步操作也是 KISS 原则的最佳例证。想知道为什么吗?最初我们使用回调函数来处理异步函数。为了简化操作,Node 开发者转而使用 Promise。为了进一步简化,Node 开发者最终推出了 async/await。明白了吗?当然,使用过 JavaScript 框架或库的人肯定都深有体会(处理回调函数的痛苦)😭,也肯定明白 KISS 原则的重要性(有了 async/await 之后,生活变得多么轻松)😎

4. YAGNI(你不需要它)

作为开发者,我们常常想得太远,过多地考虑项目的未来发展。我们试图基于“以后可能需要”或“最终肯定会需要”之类的假设来编写一些额外的功能。

答案是“YAGNI——你不需要它”;设计和开发所需的功能,避免不必要的或可预见的需求和功能。

每个开发者都经历过这个阶段,我自己也犯过同样的错误。我开发了一些客户没有要求的额外功能,想着它们将来或许有用,但最终客户想要的系统和我预想的完全不一样。

为什么需要 YAGNI?
因为你将来很可能根本不需要它,这样做只会浪费时间。如果你采用敏捷或增量式软件开发模式,你不可能一次性获得所有需求。避免给项目增加冗余。
替代文字

按需建造!别装巫师

简单来说,活在当下,而不是活在未来;确保你为未来做好准备。
我举个简单的例子,可能听起来有点模糊,但你应该能理解。

/**
** For the first iteration requirement was to design a simple React Web - ** App to manage and view meetings 
**  
** A backend developer, builds the requirements and then spends adequate
** amount of time on creating a socket for adding real-time notification 
** based on his assumptions that it would be needed for Mobile App in 
** near future.
**  
** In the second iteration, they finalize that project is confined to only
** as Web-App and there is no scope for Mobile App for this at all. 
**
**
** What's the whole point of investing so much time and implementing it 
** when it was not asked in the first place?
** 
**/
Enter fullscreen mode Exit fullscreen mode

5. SoC(关注点分离)

分离关注点是开发者或人类总是无法实现的一个主要且最基本的原则。

替代文字

看看这代码库有多乱?
想象一下,如果不按关注点分离代码,你的代码库会变成什么样子。

作为开发者,我们常常犯一个简单的错误,那就是把太多东西打包到一个类/函数里。我们设计功能时,总是想用一个函数、一个类或一个对象“包揽一切”。这种解决问题的设计方法是错误的,而且从长远来看,维护起来会非常繁琐。

要想成就一件大事,就把这件事分解成许多小事。

  • 匿名的

始终保持高度抽象;最简单的例子就是 MVP 设计(模型-视图-表示器设计);其中设计分为三个部分:模型处理数据,表示器处理用户界面或用户所看到的内容。

替代文字
关注点分离:护士与医生

如上例所示,医生和护士的职责是明确、分开和分明的,因此对每个人来说都更容易管理和维护。

另一个简单的例子如下:

以上示例展示了我们如何将样式和 HTML 内容分离;本质上就是将 CSS 文件外部化。

6. 童子军法则(重构)

如果你参加过童子军,你一定知道一条简单的规则:“离开营地时,要比你来时更干净”。

这条规则同样适用于软件开发。在实现新功能或修改遗留代码时,我们常常忽略的一点是,它对现有代码质量的影响。

我们不去检查现有代码中的技术债务,反而不断地在其基础上构建新功能。这最终会导致整个系统崩溃,并在某个时候破坏代码,而这绝对是我们不希望发生的事情。

重构是关键。重构简单来说就是改变代码结构,而不改变其实现方式或最终结果。

最简单的例子:

耳机已重构为耳塞:便于携带且价格更低

同样,我们也应该重构代码库,以提高理解性、可读性和易维护性,并可能提高效率和优化执行。

/**
** Before Refactoring
**/

function getAddress(latitude, longitude){}
function getCountry(latitude, longitude){}
function getCity(latitude, longitude){}

/**
** After Refactoring :: 
** Better readability and maintain function-arity (<3-4 No. of Arguments)
**/
function getAddress(coordinates){}
function getCountry(coordinates){}
function getCity(coordinates){}
Enter fullscreen mode Exit fullscreen mode

注意:
避免不必要的优化/重构

7. TDA(告知而非询问)

“告知而非询问”是面向对象编程的基本原则,它提醒人们,面向对象编程的核心在于用处理数据的方法封装数据。是不是有点绕?

当你想从类中访问数据时,千万不要直接使用对象来访问它,而是通过请求该数据的方法,更简单的方法是使用 getter/setter,就像你们都听说过的那样。

TDA 表明,执行一些操作总是比直接访问数据更好。

TDA 的一个简单示例如下:

/**
** Non TDA Approach
**/

class User {

constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

const userObj = new User('OhMyScript', '22');
console.log(userObj.name); // Breaches TDA
console.log(userObj.age); // Breaches TDA



/**
** TDA Approach
**/

class User {

constructor(name, age) {
    this.name = name;
    this.age = age;
  }

getName(){
   return this.name;
}

getAge(){
   return this.age;
}
}

const userObj = new User('OhMyScript', '22');

console.log(userObj.getName()); // Does not breach TDA
console.log(userObj.getAge()); // Does not breach TDA
Enter fullscreen mode Exit fullscreen mode

8. P³(P立方原理)

这并非编程原则,而是我坚信的通用开发者原则,也是帮助你精通上述所有原则的唯一途径:熟能生巧。


随着经验的积累,你的标准只会越来越高。

这些原则并非学习后就能立即应用的。这与我们常听到的关于陈年佳酿的说法非常相似。

这些是一些最重要的基本原则,它们在你的开发者生涯中扮演着重要角色。我确信可能还有许多其他原则我没有提到。

了解SOLID原则的朋友们,请继续关注下一篇文章。SOLID原则是面向对象编程中非常重要的设计原则之一。我决定专门写一篇文章来详细介绍它。

如果您喜欢这篇文章,请点赞、分享并订阅博客。如果您希望我撰写关于我擅长的特定领域/技术领域的文章,请随时发送邮件至shravan@ohmyscript.com。

敬请期待我的下一篇文章,主题是SOLID编程原则。

欢迎订阅我的博客OhMyScript,获取更多相关文章。敬请期待更多精彩内容。

今天就到这里。感谢阅读。

下次再见。
祝学习愉快。

文章来源:https://dev.to/zhravan/must-know-basic-engineering-principles-for-programming-383