如何在Rails中复用装饰器代码?基础模块使用方案咨询
解决方案:提取复用装饰器逻辑到Concern模块
1. 模块存放位置
在Rails项目里,推荐把这类跨装饰器共享的逻辑放在app/decorators/concerns/目录下——这个目录是Rails默认自动加载路径的一部分,专门用来存放可复用的代码片段,不需要额外配置就能被框架自动识别加载。
另外建议不要用Base这种宽泛的命名,换成ReactionableDecoratorConcern会更语义化,一眼就能看出它是处理支持reaction的模型装饰器的复用逻辑。
2. 模块实现与混入方式
你要复用的都是实例方法(比如reactions_total_count是针对单个Post/Comment实例做统计的),所以用include就完全正确——include会把模块里的方法添加到类的实例上,而extend是用来添加类方法的,这里根本用不上。
先写复用模块的代码:
# app/decorators/concerns/reactionable_decorator_concern.rb module ReactionableDecoratorConcern def reactions_total_count object.reactions.count end def reactions_type(kind) # 这里优化了原代码:用ActiveRecord查询直接统计,比加载所有reactions到内存再遍历高效得多 object.reactions.where(reaction_type: kind).count end def likes_count reactions_type('like') end def hearts_count reactions_type('heart') end def wows_count reactions_type('wow') end def laughs_count reactions_type('laugh') end def sads_count reactions_type('sad') end end
然后修改你的装饰器类,混入这个模块:
# app/decorators/post_decorator.rb class PostDecorator < ApplicationDecorator delegate_all include ReactionableDecoratorConcern end
# app/decorators/comment_decorator.rb class CommentDecorator < ApplicationDecorator delegate_all include ReactionableDecoratorConcern end
补充说明
- 为什么不用
Base命名?宽泛的命名会让代码可读性变差,当项目规模变大后,你很难快速回忆起Base模块到底负责什么,语义化命名能让后续维护轻松很多。 - 为什么优化
reactions_type?原代码用collect(&:reaction_type).inject会把所有reactions对象加载到内存中再遍历统计,而用where(...).count是直接在数据库层面完成统计,性能更优,尤其是当reactions数量较多时,差异会非常明显。
内容的提问来源于stack exchange,提问作者PrimeTimeTran
相关产品推荐
相关产品推荐

