小而精的关注点分离
我们经常会遇到一些中等规模的行为,我们希望将其提取出来,但只混合到单个类中。
提取一个普通的 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) 链接
定义一个关注点的一种低冗余捷径。
concern :EventTracking do
...
end
等效于
module EventTracking
extend ActiveSupport::Concern
...
end
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/core_ext/module/concerning.rb, line 132 def concern(topic, &module_definition) const_set topic, Module.new { extend ::ActiveSupport::Concern module_eval(&module_definition) } end
concerning(topic, prepend: false, &block) 链接
定义一个新的关注点并将其混合进来。
来源: 显示 | 在 GitHub 上
# File activesupport/lib/active_support/core_ext/module/concerning.rb, line 114 def concerning(topic, prepend: false, &block) method = prepend ? :prepend : :include __send__(method, concern(topic, &block)) end