Як створити метод приватного класу?


216

Як працює такий підхід створення методу приватного класу:

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

7
Я щойно побачив цю статтю, де обговорювали способи створення методів приватного класу, і подумав, що це добре: jakeyesbeck.com/2016/01/24/ruby-private-class-methods/…
Nathan Long

Відповіді:


265

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

Крім того, (у рубіні 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

105

ExiRe написав:

Така поведінка рубіну насправді засмучує. Я маю на увазі, якщо ви переходите до приватного розділу self.method, він НЕ приватний. Але якщо ви перемістите його до класу << self, він раптом спрацює. Це просто огидно.

Плутати це, мабуть, так, засмучуючи це, можливо, але огидно це точно немає.

Це має сенс, коли ви зрозумієте модель об'єкта Ruby та відповідний потік пошуку методу , особливо якщо врахувати, що privateце НЕ модифікатор доступу / видимості, а насправді виклик методу (з класом як його одержувачем), як обговорювалося тут ... не існує такого поняття, як "приватний розділ" у Рубі .

Щоб визначити приватні методи екземпляра , ви закликаєте privateклас примірника, щоб встановити видимість за замовчуванням для згодом визначених методів приватною ... і, отже, має сенс визначати методи приватного класу за допомогою викликуprivate клас класу, тобто. її метаклас.

Інші основні, самопроголошені мови ОО можуть надавати вам менш заплутаний синтаксис, але ви, безумовно, торгуєте цим заплутаною та менш послідовною (непослідовною?) Об'єктною моделлю без потужності засобів метапрограмування Рубі.


Отже, якщо я правильно розумію, сам ruby ​​не має ключових слів модифікатора доступу (загальнодоступних, приватних та захищених), а має методи модифікатора доступу (public, private, protected)? Це щось, що повинно бути розроблено на требі рубічної помилки для Matz для впровадження належних модифікаторів доступу до ключових слів чи це очікувана поведінка?
Едвард

13
@Edward Він розроблений так junichiito.blogspot.co.uk/2012/03 / ... . Чому "належним"?
iain

1
Виходячи з цього, замість того, щоб робити, private_class_method :method_nameви могли зробити private_class_method def method_name....
bjt38

send(private_method)також доступний поза об'єктом.
stevenspiel

1
@ bjt38 Просто для наочності це було бprivate_class_method def self.method_name
Том

78

За замовчуванням усі методи класу є загальнодоступними. Щоб зробити їх приватними, ви можете використовувати Модуль # private_class_method, як @tjwallace написав або визначити їх по-іншому, як це зробили:

class << self

  private

  def method_name
    ...
  end
end

class << selfвідкриває одиночний клас самості, так що методи можуть бути переглянуті для поточного власного об'єкта. Це використовується для визначення методу класу / модуля ("статичний"). Тільки там визначення приватних методів дійсно дає вам методи приватного класу.


17

Тільки для повноти ми також можемо уникати оголошення private_class_method в окремому рядку. Мені особисто не подобається це використання, але добре знати, що воно існує.

private_class_method  def self.method_name
 ....
end

5

Я теж вважаю, що Рубі (або принаймні мої знання про це) не вистачає позначки в цій області. Наприклад, наступне робить те, що я хочу, але незграбно,

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 та мої показники якості коду мають змогу створити громіздкий код. Щоб код працював так, як я хочу, і вгамовувати показники, я повинен зробити метод порівняння () класу. Оскільки я не хочу, щоб він був частиною публічного API класу, мені потрібно, щоб він був приватним, але "приватний" сам по собі не працює. Натомість я змушений використовувати "private_class_method" або якусь подібну обробку. Це, в свою чергу, змушує використовувати "self.class.send (: порівняти ..." для кожної змінної, яку я перевіряю в "== ()". Тепер це трохи непросто.


Те, що вам потрібно використовувати send , не має нічого спільного з тим, як ви "позначаєте" методи класу приватними. Приватні методи не можна викликати «ззовні».
Паскаль

4

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

Це заклик до методу self#private/ A#privateякий "перемикає" приватний доступ на всі наступні визначення методу екземпляра, поки не буде змінено інше:

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

Одержувач "повідомлення приватне" - самоврядування всерединіclass A - це об'єкт класу А. Я всередині class << Aблоку - це ще один об'єкт, клас синглтон.

Наступний приклад насправді викликає два різні методи, які називаються приватними , використовуючи два різних одержувачів або цілі для виклику. У першій частині ми визначаємо метод приватного екземпляра ("для класу 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, але продовжуєте оголошувати метод одиночного класу для іншого класу, одиночного класу.


-1

Рубі, здається, забезпечує погане рішення. Для пояснення почніть з простого прикладу 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

Зараз Рубі, здається, не забезпечує еквівалента. Правила Рубі, я думаю, полягають у тому, що приватні методи не мають доступу до приймача. Це є,

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

Для методів приватного примірника це нормально, але викликає проблеми з методами приватного класу.

Я хотів би, щоб Рубі функціонувала таким чином:

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, але в цьому випадку дизайн спеціально повинен мати метод екземпляра виклику класу метод приватного класу.


-14

Станом на рубін 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

Я намагався це private def self.second_methodробити перед кожним позначенням методу, який не працював на моєму рубіні 2.3.3. Але це позначення працює для мене.
Еміль Врійдагс

11
Це неправильно, тому що дзвінки Check.second_methodтакож спрацювали б без проблем, тому насправді це не приватно.
Дейвін

1
Спробувати це не вийдеprivate_class_method :second_method
KING SABRI
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.