本节摘要:经典设计模式在 Groovy 里被语言特性"降维打击"——策略模式内化为闭包参数、委托模式获得语言级支持、资源管理用执行围绕方法强制安全。本节讲这三种模式的 Groovy 形态,以及它们如何改变你的架构思维方式。
阅读完本节,你应当能够:
设计模式是"特定语境下重复出现问题的成熟解法"。但一个反直觉的事实是:当一种模式足够普遍且重要时,它往往会被语言本身吸收,变成语法糖或惯用法。Java 里需要十几个类的策略模式,在 Groovy 里一个闭包就够;需要包装类嵌套的装饰器,一个委托就够。这不是"模式过时了",而是"模式被内化了"——你不需要显式写它,因为它已经成了语言的默认能力。
这种内化的价值不只是少写代码,而是思维层级的跃迁:不再关注"怎么组装类",而是关注"怎么表达意图"。本节讲三种被 Groovy 内化的经典模式,你会看到语言特性如何重塑软件结构。
经典策略模式需要接口 + 多个实现类 + 注入。在 Groovy 里,策略就是闭包:
def sortWith = { list, Comparator cmp -> list.sort(cmp) } def byName = { a, b -> a.name <=> b.name } def byPrice = { a, b -> a.price <=> b.price } sortWith(products, byName) sortWith(products, byPrice) // 策略即闭包,随时替换
闭包可以作为参数传递、按需组合,策略从"类层级"下沉到"代码块层级"。当业务规则变更时,修改一个闭包比修改一个类层次结构安全且快速。这要求开发者具备函数式思维——把"做什么"和"怎么做"分离。
经典委托模式用于把责任转移给另一个对象。Groovy 的 delegate 机制让委托成为语言特性:闭包执行时,方法调用可被解析到委托对象上。
class HtmlBuilder { def div(String cls) { "<div class='$cls'>" } def p(String text) { "<p>$text</p>" } } def build = { -> div 'container' { p 'hello' } } build.delegate = new HtmlBuilder() build.resolveStrategy = Closure.DELEGATE_FIRST println build() // <div class='container'><p>hello</p></div>
委托让"行为借用"变得空前自然——对象不必继承、不必包装,只需把闭包委托给目标上下文。这在构建器、DSL、事件监听里都有广泛应用。解析策略(OWNER_FIRST/DELEGATE_FIRST)让你精确控制查找顺序,避免歧义。
资源管理(文件、连接、锁)的经典问题是"忘记关闭"。执行围绕方法把获取与释放封装进方法,业务逻辑作为闭包传入:
// Groovy 的 withXxx 就是执行围绕方法的体现 new File('data.txt').withReader { reader -> println reader.readLine() } def withLock(Lock lock, Closure body) { lock.lock() try { body() } finally { lock.unlock() } }
调用者无需关心资源的获取与释放,只需关注业务逻辑。无论是否抛异常,资源都会正确关闭——这是"强制保证"而非"提醒注意"。从数学视角看,它把"获取 → 业务 → 释放"的易错序列封装成了带 finally 的确定性函数。

三种模式不是孤立的:闭包为委托提供载体,委托为执行围绕提供上下文,执行围绕又保障闭包执行的安全。它们组合起来,就是一套可表达复杂业务逻辑的动态体系。
但力量伴随责任。三条纪律:第一,核心领域模型保持静态类结构——稳定性优先,动态模式用在边缘;第二,动态特性适度克制——过度使用会让代码难以静态分析、IDE 补全失效;第三,为动态行为补测试与文档——没有编译器兜底,测试就是安全网。
| 模式 | Groovy 形态 | 适合场景 | 注意 |
|---|---|---|---|
| 策略 | 闭包参数 | 规则、算法切换 | 复杂逻辑用具名类 |
| 委托 | delegate + 策略 | DSL 块、上下文切换 | 显式设置解析策略 |
| 资源管理 | 执行围绕方法 | 文件、连接、锁 | 嵌套不超过两层 |
⚠️ 常见坑:用闭包实现策略后,把闭包写得过于复杂(超过十几行),可读性反而比具名类差。判断标准:闭包超过一屏就该考虑提取为具名类或方法——闭包适合"短小策略",具名类适合"复杂逻辑"。
💡 关键直觉:把模式内化想成"工具进化"——手锯变成了电锯,你不再需要练出锯木头的姿势(写样板),但要更懂安全规程(理解动态特性边界)。工具的进化省的是体力,不是判断力。
在微服务或插件化系统里,Groovy 的动态模式允许不停机更新行为:脚本热加载 + 委托注入新行为,系统像生物体一样进化。闭包作为策略载体让算法切换轻量级,执行围绕方法保证动态变化不破坏稳定性。这套组合,正是"可演化系统"的设计范式——它提醒我们:架构的目标不是消灭变化,而是让变化可控、可预测、可回滚。
会。闭包参数没有编译期类型检查。想要类型安全,两个选择:闭包参数显式声明类型({ User a, User b -> ... }),或用接口 + 单方法实现配合闭包转换。多数场景用显式类型的闭包足够;契约严格的公共 API 建议回到接口。
能。闭包可以嵌套,资源管理形成层级——事务闭包内嵌套文件操作闭包,运行时按正确顺序初始化与关闭。但过度嵌套会影响性能(每层闭包有开销),且可读性下降。判断标准:嵌套层数不超过两层,否则拆分成显式方法。
三个信号:逻辑复杂度超过闭包能清晰表达的限度;需要严格类型契约;团队对动态模式不熟悉。回到经典写法不是退步——用最合适的表达方式,比坚持某种风格更重要。Groovy 的兼容性让你能在同一代码库里混合使用两种风格。
把三种模式放进一个完整场景,看它们如何协同。设想一个报表服务:需要按不同维度汇总数据、输出前加密、且保证资源正确释放。
策略(闭包)负责"怎么汇总"——按品类、按地区、按时间的汇总逻辑都是短闭包,随时替换;委托负责"上下文"——输出模块把格式化的责任委托给不同的渲染器;执行围绕方法负责"资源安全"——文件、连接、锁都用 withXxx 风格管理。
class ReportService { def summary = { rows, key -> rows.groupBy(key).collectEntries { k, v -> [k, v.size()] } } def outputWith(Closure writer, File target) { target.withWriter { w -> writer(w) } // 执行围绕:文件必关闭 } }
这个例子里,三者的分工是:策略决定"算什么",委托决定"谁来执行",执行围绕决定"资源怎么管"。三者组合,业务代码变得既短又安全——这就是 Groovy 模式内化的最终价值:不是"少写代码"本身,而是"把复杂度放到该放的地方"。
掌握 Groovy 的模式内化,不只是多几种写法,而是思维方式的升级。经典架构思维是"用结构解决问题"——类的层次、接口的契约、模式的组合;Groovy 的思维是"用表达解决问题"——闭包传行为、委托换上下文、方法封装生命周期。前者适合大型静态系统的严谨性,后者适合变化频繁系统的敏捷性。成熟的架构师两者都要会,并且知道什么时候用哪套。这也解释了为什么本书始终强调"动静分区"——它不是风格偏好,而是两种思维模式的合理分工。
落地三种模式时,用这张清单自检:策略闭包是否短小(超过一屏就提取);委托是否显式设置了 resolveStrategy(别依赖默认);资源管理是否全部用 withXxx 风格(杜绝裸 try-finally 遗忘);动态行为是否有测试与文档登记;核心模型是否保持了静态结构。五个问题都过关,模式内化的使用就是健康的。
最大阻力是"Java 思维惯性"——开发者习惯用接口表达行为,对闭包传参缺乏安全感。推广建议:先在测试代码和工具方法里用闭包(风险低),让团队体会"行为即数据"的好处;逐步把规则、策略这类高频变更的逻辑迁到闭包实现。阻力来自陌生,陌生靠实践消除。
没有对错,看语义。继承表达"Is-A"(身份),委托表达"Can-Do"(能力借用)。经典组合优于继承的原则下,委托通常更灵活、耦合更低。Groovy 的 delegate 让委托的成本大幅降低——不用写转发方法,语言直接支持。所以多数"能力借用"场景优先委托,只有真正的"身份延续"才用继承。
Groovy 里委托和特质都能实现"能力复用",但侧重不同:特质是编译期的"能力组合"(类显式声明 implements),静态、类型系统可见;委托是运行时的"上下文切换"(闭包绑定 delegate),动态、按场景生效。特质适合"这个类永久拥有这个能力",委托适合"这段代码临时借用那个上下文"。实践中,DSL 的块结构大量用委托,类的横切能力用特质,两者互补。
执行围绕方法本质是"函数式资源管理"——把资源的获取与释放封装成高阶函数,业务逻辑作为参数。它与 Java 的 try-with-resources 目标一致,但表达更灵活:闭包可以嵌套、组合、按需构造。Groovy 的 withXxx 方法(withReader、withWriter、withStream)都是标准库对这套思想的实现。理解它,你会更欣赏"让语言替你保证正确性"的设计哲学——安全不该依赖开发者的细心,而该依赖语言的约束。执行围绕方法正是这句话的完美注脚——它把"记得关闭"从道德要求变成了结构保证。当资源管理不再依赖某个开发者的责任心,而由语言的封装兜底时,团队的生产事故里就会少掉一大类"忘记释放"的问题。
会写模式还不够,团队要有一致的写法。下一节把动态自由收进秩序——编码规范与 Java 迁移指南。