注意:虽然本讨论使用 Ruby on Rails 示例,但核心概念广泛适用于其他语言和框架。
表单对象的问题:批判性检查
让我们澄清一下 Web 应用程序开发中经常模糊的“表单对象”概念。 根据各种文章(下面链接)和实践经验,表单对象缺乏普遍认可的定义和目的。 他们的角色经常被描述为:
form_for
助手: 专门设计用于 Rails 的 form_for
助手的对象。主要目标通常被认为是通过处理参数处理、类型强制和基本验证来简化控制器。 它们还用于封装来自单个表单提交的多个 ActiveRecord 模型的更新,模仿 ActiveRecord 行为以提高控制器的熟悉度。 它们被视为管理复杂行为的一种方式。
为什么使用表单对象?预期好处:
据称的优点包括:
然而,缺乏明确的定义导致了第一个主要问题:沟通不畅。当在代码库中遇到表单对象时,不清楚它们履行哪些角色(或其组合)。
本质上,表单对象旨在通过集中职责(通常在模型和/或控制器层内)来重构代码复杂性。 但这导致了第二个问题:意外的膨胀。
缺点:“胖形式对象”反模式
考虑一个常见的实现:
<code class="language-ruby">class SomethingController def create @form = MyForm.new(action_params) if @form.valid? @form.save! redirect_to "somewhere" else render :new end end def new @form = MyForm.new end end</code>
这种看似简单的方法掩盖了一个重大问题。 表单对象的公共 API(new
、valid?
、save!
及其在视图中的存在)显示它处理:
这违反了单一责任原则。 表单对象成为各种关注点的存储库,随着时间的推移吸引更多的责任(额外的视图助手、验证规则等)。 它演变成一个“胖形式的对象”,反映了它想要解决的问题。
第三个问题:冗余
更重要的问题是这些职责通常已经由其他组件处理:
在中型到大型应用程序中,这些组件可能已经存在。引入具有重叠职责的表单对象会增加不必要的复杂性和架构模糊性。 复杂性应该被直接解决,而不是被掩盖。
提议的替代方案:更加模块化的方法
更结构化的方法为每个职责使用专用对象:
<code class="language-ruby">class SomethingController def create @form = MyForm.new(action_params) if @form.valid? @form.save! redirect_to "somewhere" else render :new end end def new @form = MyForm.new end end</code>
这种方法的优点:
结论:
表单对象本质上并不是坏事。如果明智地使用它们,它们会是有益的。 然而,它们的模糊定义和责任膨胀的倾向值得仔细考虑。 在引入或使用表单对象之前,请考虑现有组件是否已经处理所需的功能。 如果存在复杂性,请通过定义良好、单一用途的对象来拥抱它,而不是将其隐藏在定义不明确的“表单对象”中。
链接文章(为清晰起见重新格式化):
以上是针对表单对象的案例的详细内容。更多信息请关注PHP中文网其他相关文章!