LoginSignup
7
6

More than 3 years have passed since last update.

[Ruby] private_class_methodの使い所

Last updated at Posted at 2019-08-03

private_class_methodについて

private_class_methodpublicなクラスメソッドprivateなクラスメソッドに変更するメソッドです。

class Example
  def self.public_foo
    puts 'public class method'
  end

  def self.private_bar
    puts 'private class method'
  end
  private_class_method :private_bar
end

Example.public_foo # => public class method
Example.private_bar # => private method `private_bar' called for Example:Class (NoMethodError)

ちなみに、privateアクセス修飾子を使ってクラスメソッドを定義すると普通にアクセスできてしまいます。

class Example
  def self.public_foo
    puts 'public class method'
  end

  private

  def self.private_bar
    puts 'private class method'
  end
end

Example.public_foo# => 'public class method'
Example.private_bar # => 'private class method'
# 普通にprivate_class_methodにアクセスできてしまう

本題:使い所

private_class_methodは親から継承したpublicなクラスメソッドprivateなクラスメソッドに変更するに使うのに役立ちます。

例えば、Singletonモジュールでは取り扱うインスタンスを一つに制限するために、親のObjectクラスが持つインスタンス生成メソッド(new・allocate)を

klass.private_class_method :new, :allocate

という形でprivateなクラスメソッドに書き換えています。

Singletonモジュールの全体を見ると、privateなクラスメソッドとなったnewメソッドはinstanceメソッドという独自のインスタンス生成メソッドの中で利用されているのがわかります。
(クラスの外からインスタンス生成を行う役割をinstanceメソッドに一任しているということになります)

singleton.rb
...

def klass.instance # :nodoc:
  return @singleton__instance__ if @singleton__instance__
  @singleton__mutex__.synchronize {
    return @singleton__instance__ if @singleton__instance__
    @singleton__instance__ = new()
  }
  @singleton__instance__
end

...

private

...

def included(klass)
  super
  klass.private_class_method :new, :allocate
  klass.extend SingletonClassMethods
  Singleton.__init__(klass)
end

...

参考:ruby/singleton.rb at ruby_2_6 · ruby/ruby

このように、単純に親クラスのメソッドをオーバーライドするのではなく、親クラスのメソッドを利用して代わりのメソッドを定義する手法として役立ちます。
同時に、private化したクラスメソッド自体の完全性を保証しています。

番外編

private_class_methodを利用せずに、特異クラスを使うことでprivateなクラスメソッドを定義することもできます。

class Example
  class << self
    def self.public_foo
      puts 'public class method'
    end

    private

    def private_bar
      puts 'private class method'
    end
  end
end

Example.public_foo # => public class method
Example.private_bar # => private method `private_bar' called for Example:Class (NoMethodError)
7
6
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
7
6