private_class_methodについて
private_class_methodはpublicなクラスメソッド
を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メソッドに一任しているということになります)
...
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)