精巧地分离关注点¶ ↑
我们经常发现自己有一个中等大小的行为块,我们希望将其提取出来,但只混入一个类。
提取一个普通的 Ruby 对象来封装它并与原始对象协作或委托通常是一个不错的选择,但当没有额外的状态需要封装时,或者当我们正在对父类进行 DSL 式声明时,引入新的协作者会使事情变得复杂而不是简化。
通常的路线是将所有内容都塞进一个单体类中,可能还会附带一个注释,作为最不坏的选择。使用独立文件中的模块意味着需要进行繁琐的筛选才能获得全局视图。
分离小关注点的令人不满意的做法¶ ↑
使用注释:¶ ↑
class Todo < ApplicationRecord # Other todo implementation # ... ## Event tracking has_many :events before_create :track_creation private def track_creation # ... end end
使用内联模块:¶ ↑
冗余的语法。
class Todo < ApplicationRecord # Other todo implementation # ... module EventTracking extend ActiveSupport::Concern included do has_many :events before_create :track_creation end private def track_creation # ... end end include EventTracking end
混入噪声被迁移到自己的文件中:¶ ↑
一旦我们的行为块开始超出滚动理解的界限,我们就会屈服,将其移动到一个单独的文件中。在这个规模下,增加的开销可能是一个合理的权衡,即使它降低了我们一目了然地了解事物运作方式的感知。
class Todo < ApplicationRecord # Other todo implementation # ... include TodoEventTracking end
引入 Module#concerning¶ ↑
通过静默混入噪声,我们得以一种自然、低开销的方式来分离精巧的关注点。
class Todo < ApplicationRecord # Other todo implementation # ... concerning :EventTracking do included do has_many :events before_create :track_creation end private def track_creation # ... end end end Todo.ancestors # => [Todo, Todo::EventTracking, ApplicationRecord, Object]
这一小步带来了一些奇妙的连锁效应。我们可以
-
一目了然地理解我们类的行为,
-
通过分离它们的关注点来清理单体的杂物抽屉类,并且
-
停止依赖 protected/private 来实现粗糙的“这是内部东西”的模块化。
预置 concerning¶ ↑
concerning 支持一个 prepend: true 参数,该参数将 prepend 关注点而不是使用 include 来进行。
方法
实例公共方法
concern(topic, &module_definition) Link
一个低样板的快捷方式来定义一个关注点。
concern :EventTracking do ... end
等同于
module EventTracking extend ActiveSupport::Concern ... end