0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事は東京海洋大学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にしてクラスメソッドとして使用していると、「外部から呼び出す意図があるのか。」という意図をコードレビューの時に指摘させてもらいました。

終わり

公開メソッドの意図として、セキュリティ的な意味合いもありますが、コードの読み手に自身の意図を告げることができると感じました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?