朴文浩博士一直强调模块化。
大脑是这样学习的。
- 将其分解为小单元(细胞,功能)
- 将这些单元组合成模块
- 这些模块汇聚在一起形成系统
编码也是一样的。
- 方法:一个动作
- 类:一个对象(存在)
- 模块(Module):将多个类/方法组合成更大概念的上层框架
在Ruby中,模块可以这样使用:
- 命名空间绑定
- 共享通用功能(Mixin)
- 定义表示领域(按主题划分的世界)的上层概念
一旦理解这一点,
代码不再只是“文件集合”,而是开始看起来像是大脑中结构化的宇宙地图。
1. 命名空间(Namespace):区分世界的框架
例如,
假设您想在多个地方使用名为User的类。
- 管理员页面的User
- 商城的User
- 用于API的User
如果只创建一个class User会怎样?
名称会冲突。思维也会混乱。
这时模块将帮助分离世界。
module Admin
class User
def info
puts "这是管理员页面的用户。"
end
end
end
module Shop
class User
def info
puts "这是商城的客户用户。"
end
end
end
admin_user = Admin::User.new
shop_user = Shop::User.new
admin_user.info # => 这是管理员页面的用户。
shop_user.info # => 这是商城的客户用户。
在这里,模块就像是:
“这是Admin世界的User”
“这是Shop世界的User”
成为区分世界的框架。
朴文浩式思维
- 框架:即使名称相同,根据上层框架的不同,角色也会有所不同
- 立体化:将User的概念存储为具有坐标的结构,而不是平面
- 模块化:在大脑中产生“按领域分类的抽屉”感觉
现在,您的大脑会认识到,
“即使名称相同,如果世界不同,那就是完全不同的存在”
这种更高层次的理解。
2. Mixin:将共同能力“插入”到多个类中的模块
第二个用途非常强大。
模块可以成为将共同功能捆绑在一起以便重复使用的块。
例如,
假设多种类型的对象都要“记录日志”。
- 用户(User)的操作日志
- 订单(Order)的状态更改日志
- 支付(Payment)的失败日志
如果分别复制粘贴到每个类中?
- 代码重复
- 维护困难
- 要修复一个bug,需要修改三个地方
这时模块 + include就发挥作用了。
module Loggable
def log(message)
time = Time.now
puts "[#{time}] #{message}"
end
end
class User
include Loggable
def sign_in
log("用户已登录。")
end
end
class Order
include Loggable
def submit
log("订单已接收。")
end
end
user = User.new
user.sign_in
# [2025-12-09 23:12:34] 用户已登录。
order = Order.new
order.submit
# [2025-12-09 23:12:35] 订单已接收。
发生了什么
Loggable模块将“记录日志的能力”模块化- 进行
include Loggable后,
该类就可以像自己的方法一样使用log方法 - 即,“将能力包插入到多个类中”
从大脑角度看
- 当一个功能需要在多个地方共享时,
大脑将其捆绑为“上层概念” - 模块正是扮演这种上层概念的角色
- 不仅仅是代码,思维结构本身也被抽象化
“啊,日志不是User或Order的一部分,
而是多个主体共享的上层能力。”
一旦有了这种领悟,
编码不再只是“编写代码”,而是
开始感觉像是在设计世界结构。
3. 抽象化:模块是创建“领域语言”的层级
通过有效使用模块,
代码逐渐变得像“领域语言”。
例如,假设有一个名为“支付系统”的领域。
module Payment
module Gateway
def charge(amount)
puts "请求支付#{amount}元。"
end
end
module Refundable
def refund(amount)
puts "退款#{amount}元。"
end
end
end
class Order
include Payment::Gateway
include Payment::Refundable
end
order = Order.new
order.charge(50000)
order.refund(20000)
这里的模块不仅仅是简单的功能。
它们是用代码表示领域概念。
Payment::Gateway→ 表示“支付请求功能”的模块Payment::Refundable→ 表示“退款功能”的模块Order是将这两者结合的存在
通过反复这样做,
Ruby代码逐渐变成了这样的感觉。
“我所思考的业务概念直接变成了代码?”
在这一点上,开发者不再是
简单的实现者,而是
设计领域的人,即设计者/哲学家。
4. 模块 vs 类:有何不同?
让我们总结一下。
| 概念 | 角色 | 大脑感觉 |
|---|---|---|
| 方法 | 一个动作 | 动词,行为 |
| 类 | 一个对象(存在),状态+行为 | 名词(人,物),实体 |
| 模块 | 多个类/方法的上层概念集合 | 世界观,能力包,领域级结构 |
在Ruby中,模块不会自己创建实例。
MyModule.new这样的操作是不允许的。
相反:
- 成为其他类的一部分,提供能力块
- 提供命名空间,成为上层框架
换句话说,
类是“存在”
模块是“存在共享的概念·能力·世界观”
5. 真正重要的是“大脑如何改变”
一旦理解了模块,
大脑就会这样进化。
- 将代码视为概念单位,而不是文件单位
- “这个功能属于哪里?”
→ “这个功能被User,Order和Payment共享,所以将其提取为模块。”
- 形成领域语言
- “Payment::Gateway”,“Notification::Sender”,“Auth::Tokenizable”之类的
→ 您的服务拥有自己的词汇
- 复杂度大大降低
- 明确了要修改的地方:
“要更改日志逻辑” → 仅需修改`Loggable`模块
- 大脑结构也像模块一样整理
- 当一个功能需要在多个地方共享时,
大脑将其**“捆绑为上层概念”**
6. 亲自尝试任务:创建自己的模块
现在,
将“模块”植入您的大脑的任务。
任务1:创建Timestamped模块
要求:
- 创建名为
Timestamped的模块 - 提供一个
stamp方法- 调用时:以“[当前时间] 消息”格式输出
- 在
Post和Comment类中include以便共同使用
预期流程:
module Timestamped
# 在此处创建stamp方法
end
class Post
include Timestamped
def publish
stamp("文章已发布。")
end
end
class Comment
include Timestamped
def write
stamp("评论已发表。")
end
end
post = Post.new
post.publish
comment = Comment.new
comment.write
任务2:将自己的领域模块绑定在一起
选择一个主题:
- 学习(Study)
- 健身(Fitness)
- 博客(Blog)
- 购物(Shop)
- 乐队练习(Band)
例如:Study领域
Study::Trackable– 记录学习时间的模块Study::Report– 输出周/月统计的模块
然后创建DayStudy,TaskStudy等类,
并include Study::Trackable来使用。
在尝试这个任务时,
您已经成为了一个“用Ruby设计自己的世界”的人。
结论:理解模块后,代码变成了‘世界设计语言’
到目前为止的流程:
- 方法:行动的最小单位
- 类:存在(对象)的框架
- 模块:多个存在共享的概念/能力/世界观
现在,当编写代码时,
您可以这样思考。
“这是一个对象的独特属性吗?”
→ 将其放在类中“多个对象共享的功能吗?”
→ 提取为模块“这是这项服务的领域概念吗?”
→ 使用模块+命名空间来定义
在那一刻,
编码不再仅仅是“学习语法”,
“我的头脑中的世界观
正以代码的形式实现”
这样的感觉。
一旦产生这种感觉,
真的会让人激动。