Özel sınıf yöntemi nasıl oluşturulur?


216

Özel sınıf yöntemi oluşturma yaklaşımı nasıl çalışır?

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)"

Ancak bu şunları yapmaz:

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
Bu makaleyi özel sınıf yöntemleri yaratmanın yollarını tartışırken gördüm ve iyi olduğunu düşündüm: jakeyesbeck.com/2016/01/24/ruby-private-class-methods/…
Nathan Long

Yanıtlar:


265

privateaçık bir nesnede (sizin durumunuzda self) bir yöntem tanımıyorsanız işe yaramıyor gibi görünüyor . private_class_methodSınıf yöntemlerini özel (veya tanımladığınız gibi) olarak tanımlamak için kullanabilirsiniz .

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

Alternatif olarak (ruby 2.1+ sürümünde), bir yöntem tanımı yöntem adının bir sembolünü döndürdüğünden, bunu aşağıdaki gibi de kullanabilirsiniz:

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 şunu yazdı:

Böyle bir yakut davranışı gerçekten sinir bozucu. Yani self.method özel bölümüne geçerseniz özel DEĞİLDİR. Ama sınıf << benliğe taşırsanız aniden çalışır. Sadece iğrenç.

Muhtemelen kafa karıştırıcı, sinir bozucu olabilir, ama iğrenç kesinlikle değil.

Ruby'nin nesne modelini ve karşılık gelen yöntem arama akışını anladıktan sonra , özellikle privateburada bir erişim / görünürlük değiştirici DEĞİL , ancak aslında burada ele alındığı gibi bir yöntem çağrısı (alıcı olarak sınıf ile) dikkate alındığında mükemmel bir anlam ifade eder ... Ruby'de "özel bölüm" diye bir şey yoktur .

Özel örnek yöntemlerini tanımlamak privateiçin, örneğin sınıfını sonradan tanımlanan yöntemlerin varsayılan görünürlüğünü özel olarak ayarlamak üzere çağırırsınız ve bu nedenle sınıfın sınıfını çağırarak özel sınıf yöntemlerini tanımlamak mükemmel bir anlam ifade eder private. onun metasınıfı.

Diğer ana akım, kendi kendini ilan eden OO dilleri size daha az kafa karıştırıcı bir sözdizimi verebilir, ancak Ruby'nin metaprogramlama olanaklarının gücü olmadan bunu kafa karıştırıcı ve daha az tutarlı (tutarsız?) Bir nesne modeline kesinlikle takas edebilirsiniz.


Yani doğru anlarsam, ruby'nin kendisinde erişim değiştirici anahtar sözcükleri (genel, özel ve korumalı) yoktur, daha ziyade erişim değiştirici yöntemleri (genel, özel, korumalı) vardır? Bu, Matz için uygun anahtar kelime erişim değiştiricileri uygulaması için yakut hata izleyicisine getirilmesi gereken bir şey mi yoksa bu beklenen davranış mı?
Edward

13
@Edward Bu şekilde tasarlanmıştır junichiito.blogspot.co.uk/2012/03/… . Neden "uygun"?
iain

1
Bundan sonra yapmak yerine private_class_method :method_nameyapabilirsin private_class_method def method_name....
bjt38

send(private_method)nesnenin dışında da erişilebilir.
stevenspiel

1
@ bjt38 Daha açık olmak gerekirse, bu olurduprivate_class_method def self.method_name
Tom

78

Varsayılan olarak tüm sınıf yöntemleri herkese açıktır. Onları özel yapmak için, @tjwallace'ın yazdığı gibi Module # private_class_method 'u kullanabilirsiniz veya yaptığınız gibi farklı şekilde tanımlayabilirsiniz:

class << self

  private

  def method_name
    ...
  end
end

class << selfmevcut self nesnesi için yöntemlerin yeniden tanımlanabilmesi için self'in singleton sınıfını açar. Sınıf / modül ("statik") yöntemini tanımlamak için kullanılır. Sadece orada, özel yöntemleri tanımlamak size özel sınıf yöntemleri verir.


17

Sadece tamlık için, private_class_method'u ayrı bir satırda bildirmekten de kaçınabiliriz. Şahsen bu kullanımı sevmiyorum ama var olduğunu bilmek güzel.

private_class_method  def self.method_name
 ....
end

5

Ben de Ruby'yi (ya da en azından benim bilgimi) bu alandaki işaretin altında buluyorum. Örneğin aşağıdakiler istediğimi yapar ama beceriksizdir,

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

Yukarıdaki kod ile ilgili sorunlarım Ruby sözdizimi gereksinimleri ve benim kod kalitesi metrikleri hantal kod için yapılan komplo olmasıdır. Kodun istediğim gibi çalışmasını ve metrikleri susturmasını sağlamak için, karşılaştırma () yöntemini bir sınıf yöntemi haline getirmeliyim. Sınıfın genel API'sının bir parçası olmasını istemediğim için özel olması gerekiyor, ancak 'özel' tek başına çalışmıyor. Bunun yerine 'private_class_method' ya da böyle bir çözüm kullanmaya zorlanıyorum. Bu, sırayla, '== ()' de test ettiğim her değişken için 'self.class.send (: karşılaştır ...') kullanımını zorlar.


Send komutunu kullanmanız gerek, sınıf yöntemlerini özel olarak işaretlemenin "nasıl" yapıldığıyla hiçbir ilgisi yoktur. Özel yöntemler "dışarıdan" çağrılamaz.
Pascal

4

Örnek yöntemleri bir sınıf tanımlama bloğu içinde tanımlanır. Sınıf yöntemleri, bir sınıfın tek sınıfında tekil yöntemler olarak tanımlanır ve gayri resmi olarak "metaclass" veya "eigenclass" olarak da bilinir. privatebir anahtar kelime değil, bir yöntemdir ( Modül # özel ).

Bu yönteme yapılan bir çağrıdır self#private/ A#privateaksi toggled kadar tüm önümüzdeki Örnek yöntemi tanımları için özel erişim "geçiş yapar":

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

Daha önce belirtildiği gibi, sınıf yöntemleri gerçekten singleton sınıfında tanımlanan singleton yöntemleridir.

def A.class_method; end

Veya anonim, singleton A sınıfının tanım gövdesini açmak için özel bir sözdizimi kullanarak:

class << A
  def class_method; end
end

"Message private" alıcısı - kendi içinde - class Asınıf nesnesi A'dır. class << ABloğun içindeki öz başka bir nesnedir, singleton sınıfıdır.

Aşağıdaki örnek gerçekte, arama için iki farklı alıcı veya hedef kullanan özel adında iki farklı yöntem çağırmaktadır. İlk bölümde, özel bir örnek yöntemi ("A sınıfı üzerinde"), ikincisinde ise özel bir sınıf yöntemi tanımlarız (aslında A'nın singleton sınıfı nesnesinde tekil bir yöntemdir).

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

Şimdi, bu örneği biraz yeniden yazın:

class A
  private
    def self.class_method; end
end

[Ruby dil tasarımcılarının] yaptığı hatayı görebiliyor musunuz? A'nın yaklaşan tüm örnek yöntemleri için özel erişime geçersiniz, ancak farklı bir sınıf olan singleton sınıfında tekli bir yöntem bildirmeye devam edersiniz.


-1

Ruby kötü bir çözüm sunuyor gibi görünüyor. Açıklamak için, özel sınıf yöntemlerine erişimi gösteren basit bir C ++ örneğiyle başlayın:

#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;
}

Yukarıdakileri çalıştırmak

   % ./a.out
   instance method
   class method

Şimdi Ruby eşdeğerini sağlamıyor gibi görünüyor. Ruby'nin kuralları, bence, bir alıcıyla özel yöntemlere erişilmemesi gerektiğidir. Yani,

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

Özel örnek yöntemleri için sorun olmaz, ancak özel sınıf yöntemleriyle ilgili sorunlara neden olur.

Ruby'nin bu şekilde çalışmasını istiyorum:

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

Ancak, ne yazık ki, yukarıdaki işe yaramıyor. Birisi daha iyi bir yol biliyor mu?

Bir yöntemden önce 'gönder' gördüğümde, kodun API tasarımcısının amacını ihlal ettiğinin açık bir işaretidir, ancak bu durumda tasarım, özel sınıf yöntemini çağırmak için sınıfın bir örnek yöntemine sahip olmaktır.


-14

Ruby 2.3.0 itibarıyla

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

Bunu private def self.second_methodyakut 2.3.3 üzerinde çalışmayan her yöntem gösterimi önce deniyordu. Ama bu gösterim benim için işe yarıyor.
Emile Vrijdags

11
Bu yanlıştır, çünkü arama Check.second_methodda sorunsuz çalışır, bu yüzden gerçekten özel değildir.
Deiwin

1
İşe yaramaz bunu deneyinprivate_class_method :second_method
KING SABRI
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.