Ruby on Rails 设计模式(第一部分):简介和策略对象

2025-06-10

Ruby on Rails 设计模式(第一部分):简介和策略对象

这篇文章是 Ruby on Rails 设计模式系列文章的第一篇。
其他部分请见:
第二部分

什么是设计模式?

模式是问题/解决方案对的形式化表达,用于做出面向对象的设计决策。
简而言之,模式是一组通用规则,用于实现其他开发人员可以轻松重复的目标。

为什么设计模式很重要?

首先,模式的目的是将现有的设计知识整理成册,这样开发人员就不必不断地重复造轮子。
其次,设计模式使设计师之间的沟通更加高效。
未经测试的胖模型、控制器、助手和视图是一场技术灾难,如果你不测试你的代码,就很难发现使用模式的必要性。
那么,如何才能从设计模式中获益呢?运用以下概念:

  • 隔离性:如果与数据库查询相关的逻辑是隔离的,那么你可以轻松地使用存根测试。同样的规则也适用于索引或第三方代码等。
  • 可读性:仅通过阅读类名就可以知道给定的代码大致在做什么。
  • 可扩展性:可以轻松修改现有代码,很多地方不需要改变逻辑。
  • 单一职责:一个方法或一个类只需负责一项操作。
  • 可测试性:感谢提到的好处,它变得更容易,因为我们只需要测试一小部分而不是大方法,连接到外部服务并同时执行业务逻辑。

策略对象

让我们从最简单的模式开始。策略对象是一种处理权限、角色和策略的模式,每次需要检查某事或某人是否有权执行某个操作时都可以使用它。像 pundit、Cancan 和 Cancancan 这样的工具都实现了这种模式。

命名约定

文件名通常带有_policy后缀,类名以Policy结尾
。方法名始终以?字符结尾。
例如:PostsPolicy#web_section?

纯策略对象由以下简单规则定义:

  • 返回必须是布尔值
  • 逻辑必须简单
  • 在方法内部,我们应该只调用传递的对象上的方法

例如:

class PostsPolicy
  def initialize(post)
    @post = post
  end

  def web_section?
    active? && @post.section == web
  end

  def active?
    @posts.where(active: true, pending: false)
  end
end
Enter fullscreen mode Exit fullscreen mode

策略对象的主要用途是简单地调用其他方法并使用数据进行比较。
它们是轻量级、简单的纯 Ruby 对象,用于管理整个项目的权限。
它们也易于测试,是复杂条件的完美替代方案。

复杂条件的一个例子:

class PostsController < ApplicationController
  def create
    if @blog.mode == live && @blog.authors.size > 0
    && (current_user.role == admin
    || (current_user.role == moderator && current_user.verified_email))
      # create
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

上述条件检查很长、很丑陋、难以阅读,可以应用策略对象模式。

让我们开始创建PostsCreationPolicy

class PostsCreationPolicy
  attr_reader :user, :blog

  def initialize(user, blog)
    @user = user
    @blog = blog
  end

  def self.create?(user, blog)
    new(user, blog).create?
  end

  def create?
    blog_with_authors? && author_is_allowed?
  end

  private

  def blog_with_authors?
    blog.mode == live && blog.authors.size > 0
  end

  def author_is_allowed?
    is_admin? || moderator_is_verified?
  end

  def is_admin?
    user.role == admin
  end

  def moderator_is_verified?
    user.role == moderator` && user.verified_email
  end
end
Enter fullscreen mode Exit fullscreen mode

我们的带有策略对象的控制器如下所示:

class PostsController < ApplicationController
  def create
    if PostsCreationPolicy.create?(current_user, @blog)
      #create
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

模型中的策略

重构复杂查询的另一种方法是在模型类上创建小型策略方法:

class User < ActiveRecord::Base
  def is_admin?
    role == admin
  end

  def is_moderator?
    role == moderator
  end

  def is_authorable?
    return true if is_admin?

    is_moderator? && verified_email
  end
end
Enter fullscreen mode Exit fullscreen mode
class Blog < ActiveRecord::Base
  def live?
    mode == live
  end

  def any_authors?
    authors.any?
  end

  def publishable?
    live? && any_authors?
  end
end
Enter fullscreen mode Exit fullscreen mode

现在我们可以重构我们的控制器:

class PostsController < ApplicationController
  def create
    if @blog.publishable? && current_user.is_authorable?
     # create
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

正如您所见,这是一种编写相同逻辑的更小且更易读的方式。

结论

策略模式是一个小概念,却能带来大效果。每次处理复杂条件时,请考虑应用策略对象。
使用 RSpec 进行测试时,您无需使用数据库记录,您的策略是纯 Ruby 对象,并且测试速度会更快。

链接:https://dev.to/renatamarques97/design-patterns-with-ruby-on-rails-part-1-introduction-and-policy-object-1c37
PREV
我不懂面向对象编程!
NEXT
学习编程是非线性的