この記事は東京海洋大学NePPの Advent Calendar 2024の18日目です。
はじめに
こんにちは!この記事は、私が実装中に、privateメソッド内にself.を書こうとして怒られた経験から、メソッドの公開レベルについて自分なりの理解をこの記事に記録するため書きました。
メソッドの公開レベルってなんぞや
Rubyでは、メソッドの公開レベルは、クラスやモジュール内で定義したメソッドへのアクセスを制御するために使われます。この制御により、外部から意図しない操作を防ぎ、コードの安全性や可読性を高めることができます。
種類
public(公開メソッド)
デフォルトの公開レベル。
外部から自由に呼び出し可能なメソッドを定義します。
APIの一部として利用者に公開したいメソッドを定義するのに使用されます。
class User
def greet
"Hello!"
end
end
user = User.new
puts user.greet # => "Hello!"
protected(保護されたメソッド)
同じクラスまたはサブクラスのインスタンスからのみ呼び出せるメソッドを定義します。
外部から直接呼び出すことはできません。
class User
def initialize(name)
@name = name
end
def compare_name(other_user)
name == other_user.name
end
protected
def name
@name
end
end
user1 = User.new("Kaiyo")
user2 = User.new("Kaiyo")
puts user1.compare_name(user2) # => true
puts user1.name # エラー: protected method `name` called
name メソッドは、protected によって外部から直接呼び出せません。
しかし、compare_name メソッド内で other_user.name として、同じクラス内の別のオブジェクトのメソッドを呼び出すことができています。
private(非公開メソッド)
同じインスタンスのコンテキスト内でのみ呼び出せるメソッドを定義します。
レシーバ(self)を明示して呼び出すことはできません。
class User
def initialize(name)
@name = name
end
def greet
"Hello, #{formatted_name}!"
end
private
def formatted_name
@name.capitalize
end
end
user = User.new("alice")
puts user.greet # => "Hello, Alice!"
puts user.formatted_name # エラー: private method `formatted_name` called
外部や他のオブジェクトから呼び出すことはできません。
クラスの内部だけで使い、外部から「formatted_name」を直接チェックされることを防ぎます。
公開レベルについてのまとめ
public: 外部から呼び出せるメソッド(クラスのインターフェース)。
protected: 同じクラスやサブクラス内で共有するメソッド。
private: 完全に内部専用のメソッド。
公開レベルを適切に使うことで、コードの可読性、保守性、安全性を向上させることができます。
なぜ怒られたのか
以上の点を踏まえれば、self. を明示する場合は「外部からの呼び出し」とみなされるため、エラーになります。まあ、ちゃんと知っていれば当たり前と言えば当たり前ですよね。
補足
なぜこのルールがあるのか?
カプセル化を強化するため
private メソッドは「クラス内部でのみ使われるもの」という意図を持っています。
self. を使うと外部からアクセス可能な public や protected メソッドと区別がつきにくくなります。
意図を明確にするため、self. を使わないことで、「これはクラス内でのみ使用される内部メソッド」という意図が明確になります。
どうしたか
team.update(b)
[name(a,b)]
private
def self.name(a,b)
元々はこんな感じのコードでprivate内にself.nameを書いて非公開にしたいはずなのに、外部からアクセス可能にしていて怒られています。
じゃあ、nameメソッドは外部には非公開(private)で、クラス内部でのみ使用できるクラスメソッドとして定義するにはどうすればいいのか、
結論
注意:一部コードを隠しているので、このコードだけだと動きません。あくまで一部を転載、改修。
team.update(b)
[name(a,b)]
class << self
private
def name(a,b)
# メソッドの処理省略
end
end
これにより
カプセル化(情報隠蔽)
name はクラス内部専用のロジックを持つメソッドです。外部からアクセスさせないことで、クラスの使い方を明確にできます。
意図の明確化
「外部から使わない」ことを明示することで、コードの読み手に「このメソッドは内部ロジック専用」という意図を伝えられます。
安全性
外部から呼び出されることで起こる予期せぬ不具合や誤用を防げます。
外部から使わないのに、publicにしてクラスメソッドとして使用していると、「外部から呼び出す意図があるのか。」という意図をコードレビューの時に指摘させてもらいました。
終わり
公開メソッドの意図として、セキュリティ的な意味合いもありますが、コードの読み手に自身の意図を告げることができると感じました。