【Ruby】Mixins扩展方式之include,extend和prepend

Mixin定义

首先,mixin是module模块,它用于在class之间共享提供可复用的的方法而不采用继承的方式。

通常在Ruby中用module来创建mixins,示例如下:

ruby 复制代码
module Greetable
  def greet
    "Hello!"
  end
end

不同于继承,可以将这个module通过mixin方式(include, extend, prepend)进任何的class。

3种方式使用Mixin

include---混入为实例方法

当使用include,对应module的方式会变成class的实例方法,示例如下:

ruby 复制代码
module Greetable
  def greet
    "Hello!"
  end
end

class Person
  include Greetable
end

p = Person.new
puts p.greet  # => "Hello!"

此时Ruby会把模块插入到类的继承链中,对应的位置是类的父类之前,可通过如下查看:

ruby 复制代码
Person.ancestors
# => [Person, Greetable, Object, Kernel, BasicObject]
extend---混入为类方法(单例方法)

使用extend时,模块中的方法会变成类方法,示例如下:

ruby 复制代码
module Identifiable
  def identity
    "I'm a class method!"
  end
end

class Robot
  extend Identifiable
end

puts Robot.identity
# => "I'm a class method!"

此时Ruby会把模块混入到对象的单例方法中,这样模块的方法就成为了这个对象的自身的方法。

prepend---类似include,顺序不同

prepend也会把模块混入为实例方法,但与include方法不同的是,模块会被插入到类之前,因此模块的方法可以覆盖类自身的方法,同时还能通过super调用原方法。示例如下:

ruby 复制代码
module Logging
  def greet
    puts "Logging: greet was called"
    super
  end
end

class User
  def greet
    "Hello from User"
  end

  prepend Logging
end

u = User.new
puts u.greet

输出结果如下:

ruby 复制代码
Logging: greet was called
Hello from User

此时查看继承链如下所示:

ruby 复制代码
User.ancestors
# => [Logging, User, Object, Kernel, BasicObject]

因此ruby会先调用Logging#greet,然后在其中通过super调用User#greet。

三者比较如下:

功能 include extend prepend
添加的方法类型 实例方法 类方法(单例方法) 实例方法
方法查找顺序 类 → 模块 → 父类 单例类 → 模块 模块 → 类 → 父类
常见用途 添加实例行为 添加类级行为 覆盖或拦截原有方法
示例场景 Enumerable、Comparable Rails 的 ClassMethods 模块 日志、监控、拦截器等

实际应用场景

通过上述过程可以发现,Ruby查找方法时,不同混入方式会改变模块在继承链中的位置从而改变查找的顺序。

include应用
ruby 复制代码
class VowelFinder
  include Enumerable
  def each
    @string.scan(/[aeiou]/) { |v| yield v }
  end
end

Enumerable 一个模块(mixin),它提供map、reduce、select等功能,但要求类中必须定义一个each方法,一旦定义了each,Enumerable的所有方法就能自动工作。这就是 include 的经典用法 ------ 给实例增加集合行为。

extend应用

Rails中的模块经常会在include的同时自动extend类,以添加类方法。示例如下:

ruby 复制代码
module Trackable
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def tracked
      puts "Tracking enabled for #{self.name}"
    end
  end
end

class User
  include Trackable
end

User.tracked  # => "Tracking enabled for User"
perpend应用

Rails、Devise、ActiveRecord等框架中经常使用prepend来在核心方法前后插入逻辑。示例如下:

ruby 复制代码
module Audit
  def save
    puts "Audit: saving record..."
    super
  end
end

class Model
  def save
    puts "Saving to DB"
  end

  prepend Audit
end

Model.new.save
# => Audit: saving record...
# => Saving to DB

prepend允许在不修改原方法的情况下,对其进行包装、拦截、增强。

相关推荐
Nturmoils5 小时前
数据集成软件 KFS:多套业务库汇聚时,先把来源和口径理清
数据库
麦壳饼6 小时前
SonnetDB:批量写入的大小、背压与刷新策略
数据库·sonnetdb
weixin_460443566 小时前
Java+Linux私有化训考平台:服务、数据库与终端部署结构解析
java·linux·数据库·私有化部署·企业培训考试系统·企业考试系统
编程火箭车7 小时前
MySQL 拥抱 AI:向量、RAG、AutoML 全进了数据库
数据库·mysql·ai编程·向量数据库·rag·automl·mysql9
用户8662978480087 小时前
KingbaseES 中文乱码、排序不对?区域和字符集的问题,一次说清
数据库
aruo7 小时前
回退旧镜像之前,先确认数据库还能兼容
数据库
张洛闻Eren8 小时前
Ansible【第三课】:管理变量和事实
数据库·oracle
weixin_382395238 小时前
0-500 人工厂的生产 PMC 系统怎么做?基于 Tauri + SQLite 的性能架构实践与 5000 物料压测实录
数据库·架构·sqlite
l1t9 小时前
DeepSeek总结的 DuckDB 2.0 文档新内容
服务器·网络·数据库
IvorySQL9 小时前
PostgreSQL 日报| PG19 主备切换挂起修复(10 月 17 日)
数据库·postgresql