Как создать метод частного класса?

Почему работает этот подход к созданию метода частного класса:

class Person

  def self.get_name
    persons_name
  end

  class << self

    private

    def persons_name
      "Sam"
    end
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name  #=> raises "private method `persons_name' called for Person:Class (NoMethodError)"

Но это не так:

class Person

  def self.get_name
    persons_name
  end

  private

  def self.persons_name
    "Sam"
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

person 99miles    schedule 10.02.2011    source источник
comment
Я только что увидел эту статью, в которой обсуждаются способы создания методов частного класса, и подумал, что это хорошо: jakeyesbeck.com/2016/01/24/ruby-private-class-methods/   -  person Nathan Long    schedule 29.01.2016


Ответы (8)


private, похоже, не работает, если вы определяете метод на явном объекте (в вашем случае self). Вы можете использовать private_class_method, чтобы определить методы класса как частные (или подобные вы описали).

class Person
  def self.get_name
    persons_name
  end

  def self.persons_name
    "Sam"
  end

  private_class_method :persons_name
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

В качестве альтернативы (в Ruby 2.1+), поскольку определение метода возвращает символ имени метода, вы также можете использовать его следующим образом:

class Person
  def self.get_name
    persons_name
  end

  private_class_method def self.persons_name
    "Sam"
  end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name
person tjwallace    schedule 10.02.2011
comment
отличный ответ, спасибо! - person Jwan622; 28.12.2020

ExiRe написал:

Такое поведение рубина действительно расстраивает. Я имею в виду, что если вы перейдете в приватный раздел self.method, то он НЕ приватный. Но если вы переместите его в класс «себя», он внезапно сработает. Это просто противно.

Это, вероятно, сбивает с толку, это может расстраивать, но это определенно не отвратительно.

Это имеет смысл, если вы поймете объектную модель Ruby и соответствующий поток поиска метода, особенно когда вы примете учитывая, что private НЕ модификатор доступа / видимости, а на самом деле вызов метода (с классом в качестве получателя), как обсуждалось здесь ... не существует такой вещи, как " закрытый раздел " на Ruby.

Чтобы определить частные методы экземпляра, вы вызываете private в классе экземпляра, чтобы установить видимость по умолчанию для определяемых впоследствии методов как частные ... и, следовательно, имеет смысл определить частный класс методы, вызывая private в классе класса, т.е. его метакласс.

Другие основные, самопровозглашенные объектно-ориентированные языки могут дать вам менее запутанный синтаксис, но вы определенно уступите это запутанной и менее согласованной объектной модели (несовместимой?) без мощных средств метапрограммирования Ruby.

person pvandenberk    schedule 28.02.2013
comment
Итак, если я правильно понимаю, сам рубин не имеет ключевых слов-модификаторов доступа (общедоступных, частных и защищенных), но имеет методы модификаторов доступа (общедоступные, частные, защищенные)? Это то, что нужно вывести на трекер ошибок Ruby, чтобы Matz реализовал правильные модификаторы доступа к ключевым словам, или это ожидаемое поведение? - person Edward; 21.03.2013
comment
@Edward Это спроектировано таким образом junichiito.blogspot.co.uk/2012/03/. Почему собственно? - person iain; 03.04.2013
comment
Следуя этому, вместо того, чтобы делать private_class_method :method_name, вы могли бы сделать private_class_method def method_name.... - person bjt38; 09.12.2014
comment
send(private_method) также доступен вне объекта. - person stevenspiel; 23.06.2015
comment
@ bjt38 Для ясности это будет private_class_method def self.method_name - person Tom; 15.02.2016
comment
Отличное объяснение того, почему private не работает должным образом для методов класса. - person Kris; 25.06.2019

По умолчанию все методы класса общедоступны. Чтобы сделать их приватными, вы можете использовать Module # private_class_method как @tjwallace написал или определите их по-другому, как вы:

class << self

  private

  def method_name
    ...
  end
end

class << self открывает одноэлементный класс self, чтобы можно было переопределить методы для текущего объекта self. Используется для определения метода класса / модуля («статического»). Только там определение частных методов действительно дает вам методы частного класса.

person roxxypoxxy    schedule 07.09.2012

Для полноты картины мы также можем не объявлять private_class_method отдельной строкой. Мне лично не нравится это использование, но приятно знать, что оно существует.

private_class_method  def self.method_name
 ....
end
person Emre Basala    schedule 05.11.2015

Я тоже нахожу Ruby (или, по крайней мере, мои знания о нем) невысокой отметкой в ​​этой области. Например, следующее делает то, что я хочу, но неуклюже:

class Frob
    attr_reader :val1, :val2

    Tolerance = 2 * Float::EPSILON

    def initialize(val1, val2)
        @val2 = val1
        @val2 = val2
        ...
    end

    # Stuff that's likely to change and I don't want part
    # of a public API.  Furthermore, the method is operating
    # solely upon 'reference' and 'under_test' and will be flagged as having
    # low cohesion by quality metrics unless made a class method.
    def self.compare(reference, under_test)
        # special floating point comparison
        (reference - under_test).abs <= Tolerance
    end
    private_class_method :compare

    def ==(arg)
        self.class.send(:compare, val1, arg.val1) &&
        self.class.send(:compare, val2, arg.val2) &&
        ...
    end
end

Мои проблемы с приведенным выше кодом заключаются в том, что требования к синтаксису Ruby и мои показатели качества кода согласовываются с тем, чтобы сделать громоздкий код. Чтобы код работал так, как я хочу, и чтобы показатели успокоились, я должен сделать compare () методом класса. Поскольку я не хочу, чтобы он был частью общедоступного API класса, мне нужно, чтобы он был частным, но «частный» сам по себе не работает. Вместо этого я вынужден использовать private_class_method или какой-то другой обходной путь. Это, в свою очередь, вынуждает использовать 'self.class.send (: compare ...' для каждой переменной, которую я тестирую в '== ()'. Теперь это немного громоздко.

person dinman2022    schedule 22.09.2014
comment
Тот факт, что вам нужно использовать send, не имеет ничего общего с тем, как вы помечаете методы класса как частные. Приватные методы не могут быть вызваны извне. - person Pascal; 22.04.2015

Методы экземпляра определены внутри блока определения класса. Методы класса определяются как одноэлементные методы для одноэлементного класса класса, также неофициально известного как «метакласс» или «собственный класс». private - это не ключевое слово, а метод (Module # private).

Это вызов метода _2 _ / _ 3_, который «переключает» частный доступ для всех будущих определений методов экземпляра, пока не будет переключен иначе:

class A
  private
    def instance_method_1; end
    def instance_method_2; end
    # .. and so forth
end

Как отмечалось ранее, методы класса на самом деле являются одноэлементными методами, определенными в одноэлементном классе.

def A.class_method; end

Или используйте специальный синтаксис, чтобы открыть тело определения анонимного одноэлементного класса A:

class << A
  def class_method; end
end

Получателем «частного сообщения» - self - внутри class A является объект класса A. self внутри блока class << A - это другой объект, одноэлементный класс.

В следующем примере на самом деле вызываются два разных метода, называемых private, с использованием двух разных получателей или целей для вызова. В первой части мы определяем метод частного экземпляра («в классе A»), во второй - мы определяем метод частного класса (на самом деле это одноэлементный метод для объекта одноэлементного класса A).

class A
  # self is A and private call "A.private()"
  private def instance_method; end

  class << self
    # self is A's singleton class and private call "A.singleton_class.private()"
    private def class_method; end
  end
end

Теперь немного перепишем этот пример:

class A
  private
    def self.class_method; end
end

Вы видите ошибку [которую сделали разработчики языка Ruby]? Вы включаете частный доступ для всех будущих методов экземпляра A, но переходите к объявлению одноэлементного метода в другом классе, одноэлементном классе.

person Martin Andersson    schedule 08.11.2016

Ruby, кажется, дает плохое решение. Чтобы объяснить, начнем с простого примера C ++, который показывает доступ к методам частного класса:

#include <iostream>

class C
{
    public:
        void instance_method(void)
        {
            std::cout << "instance method\n";
            class_method();  // !!! LOOK !!! no 'send' required. We can access it
                             // because 'private' allows access within the class
        }
    private:
        void static class_method(void) { std::cout << "class method\n"; }
};

int main()
{
    C c;

    c.instance_method(); // works
    // C::class_method() does not compile - it's properly private
    return 0;
}

Выполнение вышеуказанного

   % ./a.out
   instance method
   class method

Сейчас Ruby, похоже, не предлагает эквивалента. Я думаю, что правила Ruby заключаются в том, что к частным методам нельзя обращаться через получателя. Это,

inst.pvt_method  # FAILS
pvt_method # WORKS only within the class (good)

Это нормально для методов частного экземпляра, но вызывает проблемы с методами частного класса.

Я бы хотел, чтобы Ruby работал так:

class C
    def instance_method
        STDOUT << "instance method\n"

        # Simple access to the private class method would be nice:
        class_method   # DOES NOT WORK. RUBY WON'T FIND THE METHOD
        C.class_method # DOES NOT WORK. RUBY WON'T ALLOW IT

        # ONLY THIS WORKS. While I am happy such capability exists I think
        # the way 'send' should be used is when the coder knows he/she is
        # doing a no-no.  The semantic load on the coder for this is also
        # remarkably clumsy for an elegant language like ruby.
        self.class.send(:class_method)
    end

    private_class_method def self.class_method() STDOUT << "class method\n"; end
end

Но, увы, вышеперечисленное не работает. Кто-нибудь знает способ получше?

Когда я вижу «отправить» перед методом, это явный признак того, что код нарушает намерения разработчика API, но в этом случае дизайн специально предназначен для того, чтобы метод экземпляра класса вызывал частный метод класса.

person Dave Inman    schedule 18.05.2020

По состоянию на Рубин 2.3.0

class Check
  def self.first_method
    second_method
  end

  private
  def self.second_method
    puts "well I executed"
  end
end

Check.first_method
#=> well I executed
person Vamsi Pavan Mahesh    schedule 07.04.2017
comment
Я пробовал это с private def self.second_method перед каждой нотацией метода, которая не работала на моем Ruby 2.3.3. Но эта нотация у меня работает. - person Emile Vrijdags; 03.06.2017
comment
Это неверно, потому что вызов Check.second_method также будет работать без проблем, поэтому это не совсем конфиденциально. - person Deiwin; 25.07.2017
comment
Не получится, попробуй это private_class_method :second_method - person KING SABRI; 04.05.2018