软件开发的核心原则
引言
软件工程由两个词组成,软件和工程。在计算中,软件是一个程序或一组程序,其中包含专门设计用于完成特定任务的指令。从本质上讲,工程是设计、构建和测试某些东西(如机器、流程、结构等)以服务于特定目的并以具有成本效益的方式解决问题的过程。
软件工程的原则为工程师提供了有关如何编写无错误、清晰且可维护的代码的指南。虽然工程师不能施展魔杖将一堆变量、类和函数变成完美的代码,但他们可以使用一些原则来确定他们是否以正确的方式做事。
软件设计有两种方式:一种是设计得极为简洁,没有看得到的缺陷;另一种是设计得极为复杂,有缺陷也看不出来。第一种方式的难度要大得多。
– 《皇帝的旧衣》,CACM 1981 年 2 月 C.A.R. Hoare
软件开发原则的优点
基本原则和实践是任何工程领域的基石,如果遵循得当,可以确保用户的可靠性、稳定性和易用性。软件工程原则是世界知名软件工程师和作者推荐的方法、风格、理念和最佳实践的集合。作为软件开发的一部分,这些原则可作为指南,以确保软件的最终版本实现其目的。以下是应用软件工程原则的一些优点:
- 降低与多个工程流程相关的复杂程度。
- 防止团队犯不必要的错误和错误。
- 在产品开发中培养效率、速度、质量和审慎性。
- 团队成员将更好地了解软件是如何构建的,以及他们每个人如何为软件做出贡献。
DRY 原则 (Don’t Repeat Yourself)
Don’t Repeat Yourself,简称 DRY 原则,即不要重复自身,这是软件开发的一个基础原则。很好理解,在软件开发中不要做重复性劳动。也就是经常说的“不要重复造轮子”。
DRY 的原则指出,我们不应该在太多地方重复同样的事情太多次。在软件系统中,它旨在减少重复的代码和工作量。开发人员在不知不觉中反复重写代码。比如:重复复制粘贴相同的代码。
任何一个知识点在系统内部都应当有一个唯一、明确、权威的表述。这个原则又被称为“真理的单点性(Single Point of Truth)” 或者 SPOT 原则。
重复会导致前后矛盾、产生隐微问题的代码,原因是当你修改重复点时,往往只改变了一部分而并非全部。通常,这也说明我们在软件设计中的代码的组织没有想清楚。
对 DRY 原则的思考:
设计过程中:常量、表和元数据只应该声明和初始化一次,并导入其它地方。通过,可以通过重构去除重复代码,更改代码的组织而不更改核心算法。
如果遇到重复数据无法避免,可以思考如下问题:
- 函数封装:如果代码中含有重复数据是因为在两个不同的地方必须使用两个不同的表现形式,能否写个函数、工具或者代码生成程序,让其中一个由另一个生成,或两者都来来自同一个来源?
- 文档或其他形式:如果文档重复了代码中的知识点,能否从部分代码中生成部分文档,或者反之,或者两者都来自同一个更高级的表现形式?
- 头文件和接口:如果头文件和接口声明重复了实现代码中的知识点,是否可以找到一种方法,从代码中生成头文件和接口声明?
- 不仅要确保代码没有错误,而且没有重复的行。
- 如果一段代码在代码库中出现两次以上,则应将其移动到单独的函数中。
- 即使发现第二次重复该方法,也应创建一个单独的方法。
数据结构中也存在类似的 SPOT 原则:“无垃圾,无混淆”(No junk,no confusion)。
“无垃圾”是说数据结构(模型)应该最小化,比如,不要让数据结构太通用,居然还能表示不可能存在的情况。
“无混淆”是指在真实世界中绝对明确清晰的状态在模型中也应该同样明确清晰。
简言之,SPOT 原则就是提倡寻找一种数据结构,使得模型中的状态跟真实世界系统的状态能够一一对应。
KISS 原则 (Keep It Simple, Stupid)
Keep it simple stupid,简称 KISS 原则。简单性原则指出,代码应该尽可能简单,没有复杂的结构,否则调试和维护可能会更加困难。此外,另一个程序员将更难理解代码逻辑,这将需要更多的时间和精力。
在做软件设计的工作中,很多时候都不要想得过于复杂,也不要过度设计和过早优化,用最简单且行之有效的方案也就避免了复杂方案带来的各种额外成本。这样既有利于后续的维护,也有利于进一步的扩展。
思考: 永远都不会知道用户会怎么操作,所以没有必要在软件开发中过度设计。你想增加这种复杂性吗?在编写下一个大项目时,请确保编写简单易懂的代码。
- 最好是方法较小,不超过 40-50 行。
- 所有关键/关键方法都应该有一个注释的文档,以便其他开发人员更好地理解。
- 方法一次只能解决一个问题。
- 你的项目有很多条件语句,对吧?随时将代码分解成更小的块。
- 如果可能,请使用简单的构造来解决问题,而无需大量分支、深度嵌套或复杂的类结构。
YAGNI 原则 (You Aren’t Gonna Need It)
You Ain’t Gonna Need it: 即 YAGNI 原则。只需要将应用程序必需的功能包含进来,而不要试图添加任何其他你认为可能需要的功能。因为在一个软件中,往往 80% 的请求都花费在 20% 的功能上。
根据这一原则,程序员不应该包含功能,除非绝对必要。它指出,你不应该引入一些东西来解决尚不存在的未来问题。在大多数情况下,程序员从一开始就尝试一次实现所有功能。最终,这些功能中的大多数都变得无用。此外,缺少 YAGNI 可能会导致代码杂乱无章和非常广泛的返工。YAGNI 还避免了复杂性,特别是添加未来可能需要的功能所带来的复杂性。
思考:
- 关注软件中的核心功能,功能越少,开发周期就会越短,才有更有立足于市场。
- 在开始时只向类中添加几个方法总是一个好主意。不应向项目添加死代码。
- 一旦您的项目开始成型并出现新的需求,您就可以添加更多功能。因此,您将获得精益软件开发。
- 因此,您可以节省与尝试理解或调试代码相关的不必要的时间、精力和成本。
避免过早优化(Avoid Premature Optimization)
过早的优化被认为是Donald Knuth编程中万恶之源。总的来说,我们同意优化(提高代码的质量和效率)可以提高开发速度,同时减少资源使用。但是,如果过早完成,可能会有害。
- 造成这种情况的一个主要原因是优化代码的开发需要更多的时间和精力。
- 此外,当实施最佳方法时,软件要求可能会发生变化。发生这种情况时,您的程序最终会被扔进垃圾桶或变得难以更改。
- 此外,通常需要回归测试来确认整个系统的正确性。
因此,最好先使用简单的方法,尽管它可能不是最佳的。稍后,在估计此方法减慢应用程序速度后,您可以切换到更快或资源密集程度更低的算法。
完成胜于完美
Done is better than perfect: 在面对一个开发任务时,最佳的思路就是先把东西做出来,再去迭代优化。如果一开始就面面俱到,考虑各种细节,那么很容易钻牛角尖而延误项目进度。
这一点在工作中深有体会,每次拿到一个需求总是要能快速交付,给用户测试,能满足他们的需求比自己想到的完美更重要。
思考: “人无完人,金无足赤”,软件开发也是,先完成,再优化。
选择最合适的
Choose the most suitable things: 这是在做方案选择、技术选型时候的一个很重要的原则。在面对许多技术方案、开源实现的时候,务必做到不能盲目求新,要选择最合适的而非被吹得天花乱坠的。
思考: 技术层出不穷,日新月异,没有必要一味追求最前沿的技术,正确的事情就是针对特定的需求能够解决当前的问题。
BDUF (Big Design Upfront)
根据这个原则,在任何编码开始之前,开发人员应该先设计项目,创建关系图的流程,然后再实现。在开发功能之前,我们应该首先考虑整个系统的架构和设计到最小的细节,然后按照我们计划中概述的步骤来实现它。
这有助于在需求阶段发现问题并快速解决问题。但是,在项目的生命周期中,软件要求可能会发生变化,并且对软件要求的这种更改可能会导致困难,甚至可能使设计过时。处理此问题的最佳方法是首先设计体系结构,然后根据优先级将需求划分为多个阶段。在开发过程中从最高优先级阶段发展到最低优先级阶段。在编码之前,请在每一步中实施 BDUF 原则。
虽然这种方法在某些情况下可能很有用,但它也可能导致不必要的复杂性和缺乏灵活性。相反,许多开发人员更喜欢迭代方法,其中设计随着软件的开发和测试而随着时间的推移而发展。
SOLID
S – SRP(单一责任原则):根据单一责任原则,类、函数、模块或服务必须只有一个更改原因,即它必须只有一个责任。然而,为什么这如此重要?
- 当您编写专用于单个功能的类或函数时,将更容易理解、维护和修改代码。
- 如果要修改系统的功能,则需要知道必须修改代码的确切 DRY 位置。
- 它使代码更有条理和可读性。它还使重用代码更容易。
O – OCP(开放封闭原则):在软件开发中,我们分阶段工作。作为一个团队,我们实现一系列功能,测试它们,然后将它们交付给用户。然后,我们继续实现下一组功能。在开发新功能时,我们要做的最后一件事就是更改已经过测试并正在运行的现有功能。因此,我们尝试在现有功能的基础上添加新功能。
开闭原则促进了这一想法。根据它,我们的函数、类和模块应该以这样一种方式进行设计,即它们对扩展开放,但对修改关闭。
- 开放扩展(Open):可以在不破坏现有代码的情况下将新功能添加到类和模块中。组合和继承可用于实现此目的。
- 关闭修改(Close):理想的做法是不要进行破坏当前功能的更改,因为这样做需要重构大量现有代码并编写多个测试以确保更改有效。
L – LSP(Liskov 替换原则):根据 Liskov 替换原则,所有子/派生类都应该可替换其父类/基类,而不会影响或破坏程序的正确性。因此,子类(派生类/子类)中的对象的行为应类似于超类(父类/基类)中的对象。因此,应在项目中谨慎使用继承。虽然继承可能是有益的,但建议适度和上下文地使用它。在执行继承之前,必须考虑类的后置条件和前提条件。
I – ISP(接口隔离原则):根据接口隔离原则,不应强迫客户端依赖或依赖他们不使用的方法。如何实现这一点?简单:使您的界面简短或小巧且重点突出。具有许多行为的界面很难维护和发展。因此,将大型接口分成较小的接口,每个接口都专注于一组特定的功能,以便选择依赖或仅依赖它们所需的功能。
D – DIP(依赖反转原则):该原则旨在消除软件模块之间的紧密耦合。根据这一原则,高级模块不应依赖于较低级别的模块,而应依赖于它们的抽象。我们可以将其分为两部分:
- 高级模块必须独立于低级模块。两者都应该依赖于抽象
- 抽象应该独立于细节,而细节应该依赖于抽象。
思考:抽象非常稳定。因此,您可以轻松修改开源或闭源代码的行为。因此,您可以改进其未来的演变。
奥卡姆剃刀(Occam’s Razor)
这是编程中非常常见的基于哲学的想法。该原则以英国僧侣奥卡姆的威廉命名。该原则解释如下:除非需要,否则不要创建额外的实体。在实现之前考虑添加新方法/类/工具/流程的好处至关重要。底线是这样的:如果添加方法/类/工具/过程不会带来任何好处,但会增加复杂性,那有什么意义呢?
- 首先根据精益软件开发编写最直接的代码。添加这些内容后,您只能在必要时添加更复杂的内容。
- 使用简单的代码逐步开发、测试和更正产品要容易得多。简单的代码还可以使程序运行得更快,并且不太可能引起错误。
效率是工程原则的核心,这通常会导致简单性。“保持简单始终是最好的方法。”这个想法不仅对软件开发很重要,而且对日常生活也很重要。然而,奥卡姆剃刀并不是软件工程师以简单解决方案的名义忽略属性的借口。
得墨忒耳定律
又被称作最少知识原则(Principle of Least Knowledge),一个物体永远不应该知道另一个物体的内部细节。它旨在促进软件组件之间的松散耦合。软件组件耦合得越紧密,修改它们就越困难。这个想法来自“只和你的朋友说话”这句话。强烈建议:
- 确保软件组件彼此独立。
- 提高代码重用率,简化维护,并通过减少依赖关系使测试更容易。
- 可以减少类之间的通信和耦合。
- 将相关类保留在一个包/模块/目录中以实现内聚。
测量两次,切割一次(Measure Twice and Cut Once)
工程中的黄金法则是测量两次,切割一次。从本质上讲,这个原则是说你必须在采取行动之前彻底和仔细地计划和准备。每当您创建新系统时,请确保它是有用的、可取的、易于访问的和可信的。如果操作不正确,开发生命周期的需求阶段可能会导致超过 50% 的编码问题。因此,您应该采用系统的编码方法。
- 验证是否未在代码中省略或添加太多要求。
- 制定蓝图,以便在整个过程中实现高质量的编码。
- 从一开始就测试您的项目以确保其正常工作。
最小惊讶原则
它也被称为最小惊喜原则,它已经存在了很长时间。理想情况下,根据最小惊讶原则,特征不应具有高惊讶因子。这意味着你应该编写直观和明显的代码,这样当他们审查它时就不会让其他人感到惊讶。系统的组件应按最终用户的预期运行。否则,用户不太可能使用让他们感到惊讶、惊讶或困惑的功能或结构。制作一种名为 making_Patties 但产生番茄对象的方法将完全令人惊讶,而且显然很糟糕,无论它的名字是什么。方法、类、变量、参数的命名约定对于软件维护是非常需要的。
软件产品是您为人们使用而制造的产品。因此,您可以通过创建用户友好的功能来获得很多收益。尝试遵守人类的心智模型、经验和期望。记住要尽快抓住用户的注意力,因为普通用户的注意力持续时间已经下降。
总结
当您应用这些软件工程原则时,您将能够将工程任务引向正确的方向,并在尽可能短的时间内实现您的工程目标。这些原则使开发人员能够构建可靠、高效且可管理的软件系统,以满足用户和利益相关者的需求,同时有效地处理复杂性、提高质量和降低风险。
不要忘记将这些原则应用到您的日常工作中。如果您发现自己很难记住上述任何原则,不妨把本文放进你的收藏夹吃灰,多谢~
参考资料:
《Java 工程师修炼之道》
《Unix 编程艺术》
- 点赞
- 收藏
- 关注作者
评论(0)