发布于 2026-01-06 0 阅读
0

行为的本地化 DEV 全球展示挑战赛,由 Mux 呈现:展示你的项目!

行为的地域性

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

最近出现了一种名为“行为局部性原则”(Locality of Behaviour principle)的新趋势,它其实是对一个古老概念——凝聚力——的全新诠释。这种原则最近在HTMLTailwind CSS社区中发展迅速,但它本身也是一个非常有用的概念。

⚠️ 首先我要声明:我不相信软件工程中存在所谓的“原则”。我认为一切都是技术,如果使用得当,并在合适的场景下应用,就能在某些方面提升软件的性能(例如,更容易理解)。但如果使用不当或过度使用,则只会适得其反。没有万能的解决方案,“行为局部性”也不例外。

内容如下:

仅通过查看该代码单元本身,就应该尽可能清楚地了解该代码单元的行为。

这引自理查德·P·加布里埃尔的一本书中的一段话:

易于维护的主要特点是局部性
局部性是源代码的一个特性,它使程序员能够通过查看源代码的一小部分来理解该源代码。

易于理解

行为局部性(LoB)主要关注的是代码的易理解性。相比需要在整个代码库中跳转才能拼凑出逻辑的代码,能够轻松快速地追踪代码运行状态、彼此关联性强的代码更容易理解。

很容易想象:假设你正在开发一个后端应用程序——不是 API 应用程序,而是一个服务器端渲染的多页面应用程序。现在,要实现一项新功能,你需要:

  • 稍微修改一下模型。
  • 改变控制器的工作方式
  • 更新表单类
  • 更改模板
  • 稍微修改一下样式。

哪个更容易更新?

  1. 软件的一部分,也就是你需要修改的组件,都放在一个小文件夹里。
  2. 软件的一部分,其中组件分散在源代码的不同部分。

我认为答案显而易见,但我还是留给你来解答吧。

共址

这里的核心思想是代码共置。之前已经有文章讨论过这个话题

如果你的系统中,与同一行为相关的代码分散在各处,请尝试将它们集中起来,这样更容易理解和修改。这样做可以提高你的工作效率,减少挫败感,并提升你的工作满意度。

目标是,如果您需要了解或修改系统中的某些行为,您将能够在一个地方轻松找到大部分(如果不是全部)功能,并能够快速理解它们。

例如:Vue.js

Vue是最流行的单页应用程序前端库之一。它具有一种叫做单文件组件(SPC)的功能,可以将样式、JavaScript 和模板整合在一起。我们来看一个例子:

<script>
export default {
  data() {
    return {
      show: true,
      list: [1, 2, 3]
    }
  }
}
</script>

<template>
  <button @click="show = !show">Toggle List</button>
  <button @click="list.push(list.length + 1)">Push Number</button>
  <button @click="list.pop()">Pop Number</button>
  <button @click="list.reverse()">Reverse List</button>

  <ul v-if="show && list.length">
    <li v-for="item of list">{{ item }}</li>
  </ul>
  <p v-else-if="list.length">List is not empty, but hidden.</p>
  <p v-else>List is empty.</p>
</template>

<style>
  ul {
    list-style-type: none;
    padding: 0;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

现在,你已经掌握了理解这个特定组件所需的一切信息。但这并不意味着在单页应用LoB程序 (SPA) 中,将样式、模板和代码放在一起是唯一的方法。在 React 中,人们通常将它们放在不同的文件中,但都放在一个以组件名称命名的文件夹中。

例如:HTMX

如果我没记错的话,“行为局部性”(Locality of Behaviour)这个术语是由HTML的创建者Carson Gross 提出的。在 HTML 中,它指的是 HTML 允许你以一种简洁易懂(一旦你理解了 HTML)的方式将行为嵌入到 HTML 中。它并非万能,但它确实能让大量的交互变得轻松便捷,无需任何复杂的框架或编写专门的 JavaScript 代码。

以下是一个“点击编辑”功能的示例:

<div hx-target="this" hx-swap="outerHTML">
    <div><label>First Name</label>: Joe</div>
    <div><label>Last Name</label>: Blow</div>
    <div><label>Email</label>: joe@blow.com</div>
        <!-- when button gets clicked - we'll load edit form and swap it with the outer div -->
    <button hx-get="/contact/1/edit" class="btn btn-primary">
    Click To Edit
    </button>
</div>
Enter fullscreen mode Exit fullscreen mode

你看,所有行为都是本地的,我们不需要翻阅文件来查找问号document.querySelector('.some-magic-class').addEventListener(...)

点击这里查看更多交互式演示示例。

不仅仅是前端

LoB/co-location 源自前端领域,但也适用于后端。

我们不一定总是把逻辑放在同一个文件中,但总是可以将相关的代码放在一起。一个假设的 Python 应用程序的结构可能如下所示:

.
├── thing_module/
│   ├── new_thing.py
│   │   ├── NewThingForm
│   │   ├── NewThingView
│   ├── templates/
│   │   └── new_thing.html
|   └── scss/
|   |   └── new_thing.scss
├── another_module/

Enter fullscreen mode Exit fullscreen mode

把所有内容放在一起,便于查找和修改。

紧张关系与互动

不知何故,在软件开发中,我们倾向于将技术(有些人称之为“原则”)视为绝对真理——仿佛它们是独立存在的,彼此之间没有任何相互作用。

然而,事情并非如此简单。所以,让我们来看看它是如何与软件开发的不同技术和方面相互作用的。

关注点分离(SoC)

关注点分离是一种古老的技术,它将计算机系统划分为不同的部分,每个部分负责处理不同的问题。大多数情况下,这意味着我们有多个独立的“层”。例如:

  • IP协议栈采用分层结构,不同层使用不同的协议,每层负责不同的功能。
  • 在 Web 开发中,HTML(或模板)、CSS 和 JavaScript 分别处理网页或应用程序显示的不同方面。
  • 在 MVC 模式下,当使用多种后端框架时,模型、视图和控制器是如何运作的?

React刚问世时,曾遭到社区的强烈抵制,因为它违反了关注点分离原则。我们开始将HTML(模板或视图)和JavaScript代码(控制器)混在一起。

不仅如此,随着 React 的普及,我们也开始在组件中嵌入 CSS(这样的CSS-in-JS):

import styled from 'styled-components';
// Create a component that renders a <p> element with blue text
const BlueText = styled.p`
  color: blue;
`;

<BlueText>My blue text</BlueText>
Enter fullscreen mode Exit fullscreen mode

两者之间确实存在一些张力。但这主要体现在你把一切都当作原则而非技巧来看待。如果把某件事当作原则,就意味着它是不可妥协的,必须永远遵循;而技巧则是在合理且能改进事情的地方运用。

成功的秘诀其实很简单:

  • 当你的组件/代码非常简单时,通常最好能够一次性看到所有内容。
  • 但是,随着特定组件/代码单元的代码变得越来越复杂,在某个阶段,能够独立理解业务逻辑,而不将其与其他方面(例如模板或样式)混淆起来,就显得非常有用。
  • 所以,先从简单的事情入手,当事情变得越来越难理解时,就把这些问题分开,以便更容易理解。
  • 但是,当你把这些问题拆分到不同的文件中——并将它们放在一起,这样仍然可以通过查看一个单独的小文件夹轻松理解整个内容,而无需在 3 到 10 个不同的地方来回跳转——就可以了。

关键在于根据具体情况采用合适的做法,而不是一味地在所有地方都采用相同的做法。

高内聚性和低耦合性

这两个概念是创建可维护代码库的关键。大多数所谓的原则只不过是对这些概念(以及少数其他概念)的重复和重新发明。

  • 内聚性本质上衡量的是同一代码单元中代码之间的关联程度。
  • 耦合度衡量的是“如果我更改 X,我需要更改多少其他代码”。

这里的想法

  • 如果我们把性质相近(高内聚性)的东西放在一个代码单元/模块中。
  • 然后确保该模块内部的变化对模块外部的影响较小(低耦合)。
  • 那么我们的代码将是:
    • 容易理解(你只需要看一个地方)
    • 易于修改(对模块的修改不会引发连锁反应)

行为局部性意味着将功能相符的组件放在一起。特定软件的行为被巧妙地限制在单一位置,从而增强了系统的内聚性。这有助于提高系统的内聚性,进而降低耦合度。

DRY 和跨领域关注点

关键在于:重复有时是有益的。但这并不意味着你可以放任重复在你的系统中泛滥,否则最终你会得到一个杂乱无章、难以维护的烂摊子。

我不确定这种LoB与 DRY 原则相冲突的想法源自何处,但我有两个猜测:

  1. DRY用得太多了。基本上就是说,如果有人过于执着于写DRY式的俏皮话。
  2. 未解决的跨领域问题。

第一点很简单。人们有时过于执着于 DRY 原则,试图消除每一个细微的重复,结果往往会落入虚假抽象的陷阱。

另一个问题是未解决的横切关注点。例如身份验证,如果需要ensure_has_permission(X)手动加载用户并检查权限,而不是调用相应的函数,那就更麻烦了。

LoB这并不意味着你无需任何先验知识就能理解整个软件。你需要学习横切关注点以及它们在系统中的运作方式。你需要学习你正在使用的框架。例如,HTMX你需要对 HTML、HTMX 和一般的 Web 开发有一定的了解,才能声称“我只需看一眼就能理解这段代码”。

最后想说的

虽然这个术语最初是在前端环境中提出的HTMX,但LoB它却是思考任何代码库的绝佳方式。你可以问自己一些问题,例如:

我到底要去多少地方查阅资料才能理解这篇文章?

这是更好地理解代码可维护性的好方法。

文章来源:https://dev.to/ralphcone/new-hot-trend-locality-of-behavior-1g9k