Методы частного модуля в Ruby - PullRequest
96 голосов
/ 26 ноября 2008

У меня есть вопрос из двух частей

Лучшая практика

  • У меня есть алгоритм, который выполняет некоторую операцию над структурой данных с использованием открытого интерфейса
  • В настоящее время это модуль со множеством статических методов, все частные, за исключением одного метода открытого интерфейса.
  • Существует одна переменная экземпляра, которая должна использоваться всеми методами.

Это варианты, которые я вижу, какой из них лучший?

  • Модуль со статическими («модуль» в рубине) методами
  • Класс со статическими методами
  • Mixin модуль для включения в структуру данных
  • Рефакторинг часть алгоритма, которая модифицирует эту структуру данных (очень мала) и делает это миксином, который вызывает статические методы модуля алгоритма

Техническая часть

Есть ли способ сделать метод закрытого модуля ?

module Thing
  def self.pub; puts "Public method"; end
  private
  def self.priv; puts "Private method"; end
end

Кажется, что private не имеет никакого эффекта , я все еще могу позвонить Thing.priv без проблем.

Ответы [ 9 ]

84 голосов
/ 08 января 2009

Я думаю, что лучший способ (и в основном то, как пишутся существующие библиотеки) делает это, создавая класс в модуле, который имеет дело со всей логикой, а модуль просто предоставляет удобный метод, например,

module GTranslate
  class Translator
    def perform( text ); 'hola munda'; end
  end

  def self.translate( text )
    t = Translator.new
    t.perform( text )
  end
end
72 голосов
/ 07 января 2009

Есть также Module.private_class_method, что, возможно, выражает больше намерений.

module Foo
  def self.included(base)
    base.instance_eval do
      def method_name
        # ...
      end
      private_class_method :method_name
    end
  end
end

Для кода в вопросе:

module Thing
  def self.pub; puts "Public method"; end
  def self.priv; puts "Private method"; end
  private_class_method :priv
end

Ruby 2.1 или новее:

module Thing
  def self.pub; puts "Public method"; end
  private_class_method def self.priv; puts "Private method"; end
end
35 голосов
/ 26 января 2016
module Writer
  class << self
    def output(s)
      puts upcase(s)
    end

    private

    def upcase(s)
      s.upcase
    end
  end
end

Writer.output "Hello World"
# -> HELLO WORLD

Writer.upcase "Hello World"
# -> so.rb:16:in `<main>': private method `upcase' called for Writer:Module (NoMethodError)
28 голосов
/ 26 ноября 2008

Вы можете использовать «включенный» метод, чтобы делать причудливые вещи, когда модуль смешан. Это делает то, что вы хотите, я думаю:

module Foo
  def self.included(base)
    class << base 
      def public_method
        puts "public method"
      end
      def call_private
        private_method
      end
      private
      def private_method
        puts "private"
      end
    end
  end
end

class Bar
  include Foo
end

Bar.public_method

begin
  Bar.private_method
rescue
  puts "couldn't call private method"
end

Bar.call_private
12 голосов
/ 26 ноября 2008

К сожалению, private применяется только к методам экземпляра. Общий способ получить частные "статические" методы в классе - сделать что-то вроде:

class << self
  private

  def foo()
   ....
  end
end

Правда, я не играл с этим в модулях.

2 голосов
/ 09 ноября 2016

Лучший шаблон, который я нашел, выполнив это в Rails, это отказаться от модулей, которые хотят иметь приватные методы, и использовать вместо них класс Singleton. Это не правильно, но работает и кажется чище, чем другие примеры, которые я видел в этом вопросе.

Хотелось бы услышать другие мнения по этому поводу.

Пример:

ErrorService.notify("Something bad happened")

class ErrorService
  include Singleton

  class << self
    delegate :notify, to: :instance
  end

  def notify(message, severity: :error)
    send_exception_notification(message)
    log_message(message, severity)
  end

  private

  def send_exception_notification(message)
    # ...
  end

  def log_message(message, severity)
    # ...
  end
end
2 голосов
/ 15 июня 2015

Хороший способ, как это

module MyModule
  class << self
    def public_method
      # you may call the private method here
      tmp = private_method
      :public
    end

    private def private_method
      :private
    end
  end
end

# calling from outside the module
puts MyModule::public_method
1 голос
/ 17 октября 2012

Как насчет хранения методов в виде лямбд в переменных / константах класса?

module MyModule
  @@my_secret_method = lambda {
    # ...
  }
  # ...
end

Для теста:
UPD: огромное обновление этого кода через 6 лет показывает более чистый способ объявления частного метода d

module A
  @@L = lambda{ "@@L" }
  def self.a ; @@L[] ; end
  def self.b ; a ; end

  class << self
    def c ; @@L[] ; end
    private
    def d ; @@L[] ; end
  end
  def self.e ; c ; end
  def self.f ; self.c ; end
  def self.g ; d ; end
  def self.h ; self.d ; end

  private
  def self.i ; @@L[] ; end
  class << self
    def j ; @@L[] ; end
  end

  public
  def self.k ; i ; end
  def self.l ; self.i ; end
  def self.m ; j ; end
  def self.n ; self.j ; end
end

for expr in %w{ A.a A.b A.c A.d A.e A.f A.g A.h A.i A.j A.k A.l A.m A.n }
  puts "#{expr} => #{begin ; eval expr ; rescue => e ; e ; end}"
end

Здесь мы видим, что:

A.a => @@L
A.b => @@L
A.c => @@L
A.d => private method `d' called for A:Module
A.e => @@L
A.f => @@L
A.g => @@L
A.h => private method `d' called for A:Module
A.i => @@L
A.j => @@L
A.k => @@L
A.l => @@L
A.m => @@L
A.n => @@L

1) @@L нельзя получить доступ извне, но доступен почти везде
2) class << self ; private ; def успешно делает метод d недоступным снаружи и изнутри с помощью self., но не без него - это странно
3) private ; self. и private ; class << self не делают методы приватными - они доступны как с self.

, так и без него
0 голосов
/ 10 ноября 2016

Сделать приватный модуль или класс

Константы никогда не являются частными. Однако можно создать модуль или класс, не назначая его константе.

Таким образом, альтернативой :private_class_method является создание частного модуля или класса и определение открытых методов для него.

module PublicModule
  def self.do_stuff(input)
    @private_implementation.do_stuff(input)
  end

  @private_implementation = Module.new do
    def self.do_stuff(input)
      input.upcase # or call other methods on module
    end
  end
end

Использование:

PublicModule.do_stuff("whatever") # => "WHATEVER"

См. Документы для Module.new и Class.new .

...