💎
【Ruby 47日目】オブジェクト指向 - selfキーワード
はじめに
Rubyのselfキーワードについて、Ruby 3.4の仕様に基づいて詳しく解説します。
この記事では、基本的な概念から実践的な使い方まで、具体的なコード例を交えて説明します。
基本概念
selfは、現在のレシーバ(オブジェクト)を参照する特別なキーワードです:
- 現在のオブジェクトへの参照 - メソッド内でレシーバとなっているオブジェクトを指す
- コンテキストによって変わる - クラス定義内、インスタンスメソッド内で異なる
- メソッド呼び出し - selfを省略できる場合とできない場合がある
- クラスメソッド定義 - def self.method_name での self はクラス自身
selfを正しく理解することで、Rubyのオブジェクトモデルの理解が深まります。
基本的な使い方
インスタンスメソッド内のself
class Person
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end
def introduce
# selfは現在のインスタンスを指す
puts "I am #{self.name}, #{self.age} years old"
puts "My class is #{self.class}"
puts "My object_id is #{self.object_id}"
end
def who_am_i
self # インスタンス自身を返す
end
end
person = Person.new("Alice", 25)
person.introduce
#=> I am Alice, 25 years old
# My class is Person
# My object_id is ...
puts person.who_am_i == person #=> true
selfを使ったメソッド呼び出し
class Calculator
def initialize(value)
@value = value
end
def add(n)
@value += n
self # selfを返すことでメソッドチェーン可能に
end
def multiply(n)
@value *= n
self
end
def result
@value
end
end
calc = Calculator.new(10)
result = calc.add(5).multiply(2).result
puts result #=> 30
クラス定義内のself
class Example
# クラス定義のトップレベルでselfはクラス自身
puts "In class body, self is: #{self}" #=> Example
def self.class_method
# クラスメソッド内でもselfはクラス自身
puts "In class method, self is: #{self}" #=> Example
end
def instance_method
# インスタンスメソッド内でselfはインスタンス
puts "In instance method, self is: #{self.inspect}"
end
end
Example.class_method #=> In class method, self is: Example
obj = Example.new
obj.instance_method #=> In instance method, self is: #<Example:0x...>
setterメソッド呼び出しでのself
class Person
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end
def update_info(name, age)
# setterメソッドを呼ぶにはselfが必要
self.name = name # self.を付けないとローカル変数になる
self.age = age
end
def bad_update(name)
# BAD: ローカル変数への代入になってしまう
name = name
# @nameは変更されない
end
end
person = Person.new("Alice", 25)
person.update_info("Bob", 30)
puts person.name #=> Bob
puts person.age #=> 30
class << self でのself
class MyClass
# class << self ブロック内でselfは特異クラス
class << self
def who_am_i
puts "self is: #{self}" #=> MyClass
puts "self.class is: #{self.class}" #=> Class
end
def create_instance
# ここでのselfはMyClassなので、newで新しいインスタンスを作成
self.new
end
end
end
MyClass.who_am_i
#=> self is: MyClass
# self.class is: Class
instance = MyClass.create_instance
よくあるユースケース
ケース1: メソッドチェーンの実装
selfを返すことでメソッドチェーンを可能にします。
class QueryBuilder
def initialize
@conditions = []
@order = nil
@limit = nil
end
def where(condition)
@conditions << condition
self # selfを返す
end
def order_by(column)
@order = column
self # selfを返す
end
def limit(count)
@limit = count
self # selfを返す
end
def to_sql
sql = "SELECT * FROM users"
unless @conditions.empty?
sql += " WHERE " + @conditions.join(" AND ")
end
sql += " ORDER BY #{@order}" if @order
sql += " LIMIT #{@limit}" if @limit
sql
end
end
query = QueryBuilder.new
.where("age > 18")
.where("active = true")
.order_by("created_at DESC")
.limit(10)
puts query.to_sql
#=> SELECT * FROM users WHERE age > 18 AND active = true ORDER BY created_at DESC LIMIT 10
ケース2: ビルダーパターン
オブジェクトを段階的に構築します。
class User
attr_reader :name, :email, :age, :role
def initialize
@name = nil
@email = nil
@age = nil
@role = :member
end
def name=(value)
@name = value
self
end
def email=(value)
@email = value
self
end
def age=(value)
@age = value
self
end
def role=(value)
@role = value
self
end
def build
raise "Name is required" if @name.nil?
raise "Email is required" if @email.nil?
self
end
def to_s
"User: #{@name} (#{@email}), age #{@age}, role: #{@role}"
end
end
user = User.new
.name=("Alice")
.email=("alice@example.com")
.age=(25)
.role=(:admin)
.build
puts user
#=> User: Alice (alice@example.com), age 25, role: admin
ケース3: 委譲パターン
selfを使って他のオブジェクトに処理を委譲します。
class Logger
def initialize
@output = $stdout
end
def log(message)
@output.puts("[#{Time.now}] #{message}")
end
end
class Application
def initialize
@logger = Logger.new
end
def start
log("Application starting...")
# アプリケーションの起動処理
log("Application started successfully")
end
private
def log(message)
# selfのメソッドとして呼べるように委譲
@logger.log(message)
end
end
app = Application.new
app.start
#=> [2025-11-23 10:00:00] Application starting...
# [2025-11-23 10:00:00] Application started successfully
ケース4: シングルトンパターン
selfを使ってシングルトンを実装します。
class Configuration
@instance = nil
private_class_method :new
def self.instance
@instance ||= new
end
def initialize
@settings = {}
end
def set(key, value)
@settings[key] = value
self
end
def get(key)
@settings[key]
end
def configure
yield self if block_given?
self
end
end
# シングルトンインスタンスの取得
config = Configuration.instance
config.configure do |c|
c.set(:app_name, "MyApp")
c.set(:version, "1.0.0")
c.set(:debug, true)
end
# 同じインスタンスが返される
config2 = Configuration.instance
puts config == config2 #=> true
puts config.get(:app_name) #=> MyApp
ケース5: DSLの実装
instance_evalを使ってDSLを作成します。
class TaskList
def initialize
@tasks = []
end
def task(name, &block)
task_obj = Task.new(name)
task_obj.instance_eval(&block) if block_given?
@tasks << task_obj
end
def execute_all
@tasks.each(&:execute)
end
end
class Task
attr_reader :name
def initialize(name)
@name = name
@description = ""
@priority = :normal
@steps = []
end
def description(text)
@description = text
self
end
def priority(level)
@priority = level
self
end
def step(name, &block)
@steps << { name: name, block: block }
self
end
def execute
puts "Executing task: #{@name}"
puts " Description: #{@description}"
puts " Priority: #{@priority}"
@steps.each do |step|
puts " Step: #{step[:name]}"
step[:block].call if step[:block]
end
end
end
# DSLを使用
tasks = TaskList.new
tasks.task("Deploy Application") do
description "Deploy the application to production"
priority :high
step("Build") do
puts " Building application..."
end
step("Test") do
puts " Running tests..."
end
step("Deploy") do
puts " Deploying to production..."
end
end
tasks.execute_all
#=> Executing task: Deploy Application
# Description: Deploy the application to production
# Priority: high
# Step: Build
# Building application...
# Step: Test
# Running tests...
# Step: Deploy
# Deploying to production...
注意点とベストプラクティス
注意点
- setterメソッド呼び出しではselfが必要
class Person
attr_accessor :name
def update_name(new_name)
# BAD: ローカル変数への代入
name = new_name
# @nameは変更されない
end
def update_name_correctly(new_name)
# GOOD: selfを使ってsetterを呼ぶ
self.name = new_name
end
end
person = Person.new
person.update_name("Bob")
puts person.name.inspect #=> nil(変更されていない)
person.update_name_correctly("Alice")
puts person.name #=> Alice
- privateメソッドの呼び出し
class Example
def public_method
# BAD: Ruby 2.7以前ではエラー
# self.private_method
# GOOD: selfなしで呼ぶ
private_method
end
private
def private_method
"This is private"
end
end
obj = Example.new
puts obj.public_method #=> This is private
- selfの値の変化に注意
class Outer
def self.who
puts "Outer class: #{self}" #=> Outer
end
class Inner
def self.who
puts "Inner class: #{self}" #=> Outer::Inner
end
end
def instance_method
puts "Outer instance: #{self.class}" #=> Outer
# ブロック内のselfは変わらない
[1, 2, 3].each do |n|
puts "In block: #{self.class}" #=> Outer(変わらない)
end
# instance_evalでselfが変わる
[1, 2, 3].each do |n|
n.instance_eval do
puts "In instance_eval: #{self.class}" #=> Integer
end
end
end
end
Outer.who
Outer::Inner.who
obj = Outer.new
obj.instance_method
ベストプラクティス
- メソッドチェーンではselfを返す
# GOOD: selfを返してメソッドチェーンを可能に
class Formatter
def initialize(text)
@text = text
end
def upcase
@text = @text.upcase
self
end
def trim
@text = @text.strip
self
end
def reverse
@text = @text.reverse
self
end
def to_s
@text
end
end
result = Formatter.new(" hello ")
.trim
.upcase
.reverse
puts result #=> OLLEH
- selfの省略は慎重に
class Example
attr_accessor :value
def calculate
@value = 10
# GOOD: 明示的にselfを使う(可読性重視の場合)
result = self.value * 2
# ALSO GOOD: selfを省略(簡潔性重視の場合)
result = value * 2
result
end
def update(new_value)
# MUST: setterメソッドではselfが必要
self.value = new_value
end
end
- コンテキストを明確にする
# GOOD: コメントでselfの意味を明確に
class User
class << self # selfはUserクラス
attr_accessor :default_role
def create(attributes)
# ここでのselfはUserクラス
user = new(attributes)
user.role = self.default_role || :member
user
end
end
attr_accessor :role
def initialize(attributes)
# ここでのselfはUserインスタンス
@name = attributes[:name]
end
end
User.default_role = :guest
user = User.create(name: "Alice")
puts user.role #=> guest
Ruby 3.4での改善点
- Prismパーサーによる最適化 - selfの解析が高速化
- YJITの最適化 - selfへのアクセスがより効率的に
- エラーメッセージの改善 - selfに関するエラーメッセージが明確に
- 型システムとの統合 - RBSでselfの型を明示的に指定可能
# Ruby 3.4では、selfの使用がより効率的
class PerformanceTest
def initialize(value)
@value = value
end
def process
# selfへのアクセスが最適化されている
100000.times do
self.class
self.object_id
self.instance_variables
end
end
end
test = PerformanceTest.new(100)
start = Time.now
test.process
puts "Time: #{Time.now - start}s"
まとめ
この記事では、selfキーワードについて以下の内容を学びました:
- 基本概念と重要性 - 現在のレシーバ、コンテキストによる変化
- 基本的な使い方と構文 - インスタンスメソッド、クラスメソッド、setter呼び出し
- 実践的なユースケース - メソッドチェーン、ビルダー、委譲、シングルトン、DSL
- 注意点とベストプラクティス - setterでのself、privateメソッド、明示的な使用
selfを正しく理解することで、Rubyのオブジェクト指向プログラミングがより深く理解できます。
Discussion