SQL 让我感到不舒服。
抱歉,我需要您提供要翻译的具体文本内容。请在 Source 链接下面粘贴文章的正文(除代码块和 URL 之外的部分),我会按照要求将其翻译成简体中文并保留原有的格式。谢谢!
介绍
在我的实际(而非理论)理解中,object‑oriented programming 不仅仅是传统函数式范式的另一种选择,往往更像是更进一步的方式。我把它看作是带有状态管理的函数式编程。实际中,类的行为类似于具有某种固有记忆的函数,这种记忆在程序运行期间持续存在,从而能够构建更好的有状态抽象,以建模现实世界的逻辑和概念。
当我尝试 React 的 functional components 时,我本能地把网页想象成一棵树——父组件由更小的组件构成,根节点是最终要显示的组件。可复用的组件类似于 OOP 中的接口或抽象类等抽象。为了让这些组件有意义,它们必须持有数据,并且在数据变化时 UI 需要更新。此时,持有并响应数据的组件让人联想到类的“有状态抽象”解释。
这就是把 UI 视为 Object Tree 看起来很有道理的原因。传统的移动 UI 框架也强化了这一思想:Android 使用 Java/Kotlin,iOS 使用 Swift/Objective‑C,将 UI 建模为对象层次结构。那么,React 为什么抛弃这种模型,转而使用 functional components,几乎把状态管理全部交给框架,从而让开发者的显式控制大幅减少呢?
答案并不在于函数与对象的对立,而在于 imperative vs. declarative 编程。声明式 UI 描述在给定状态下界面应当 是什么,让框架负责 如何 实现。现代移动 UI 框架如 SwiftUI 和 Jetpack Compose 也遵循同样的思路。
UI 框架:命令式 vs. 声明式
传统 UI Kit(命令式)
class CounterViewController: UIViewController {
var count = 0
let label = UILabel()
let button = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
label.text = "Count: 0"
button.setTitle("Increment", for: .normal)
button.addTarget(self, action: #selector(increment), for: .touchUpInside)
}
@objc func increment() {
count += 1
label.text = "Count: \(count)"
}
}
现代 SwiftUI(声明式)
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: \(count)")
Button("Increment") {
count += 1
}
}
}
}
React(声明式)
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
SwiftUI 与 React 的相似之处
| SwiftUI | React |
|---|---|
@State var count | const [count, setCount] = useState(0) |
count += 1 | setCount(count + 1) |
body | return (…) |
View (struct) | Function component |
| Recompute body | Re‑render function |
这些相似之处展示了两个框架如何让你声明 UI 输出,并让框架在状态变化时处理重新评估。
命令式 vs. 声明式 编程
- Imperative UI:你一步一步描述如何构建和修改 UI(例如经典 UIKit、旧版 Android)。这提供了直接的控制,但往往会导致大量样板代码、复杂的状态管理以及随着应用增长而需要手动更新。
- Declarative UI:你描述给定状态下 UI 应该是什么样子。框架会决定实现该状态所需的步骤,从而产生更简洁、可读性更高的代码,并能自动响应状态变化。
对声明式范式的个人不适
我对这种范式感到一定的不适,就像我对 SQL 的不安以及我更倾向于使用 PyTorch 而非 TensorFlow 一样。这里的权衡在于 显式控制 与 实现简易性 之间。声明式抽象让代码更简洁、更优雅——TensorFlow 的 model.fit() 就是一个完美的例子——但它们也会掩盖代码何时以及多频繁运行。错误会与 状态转变 关联,而不是具体的代码行,从而使定位变得更困难。
声明式编程要求我们停止像工程师那样发出指令的思维方式,转而像设计师一样描述约束。作为软件初学者,我并不声称函数式组件或声明式编程本质上是坏的。它们显然有效,且可能更适合构建大规模系统。对许多开发者而言,这是一笔值得支付的代价。然而对我来说,这仍然是一个令人不舒服的权衡。我可以在声明式系统中工作并理解它们的价值,但仍然怀念失去的显式控制感。