Ruby'de özel modül yöntemleri


108

İki parçalı bir sorum var

En iyi pratik

  • Genel arabirimi kullanarak bir veri yapısı üzerinde bazı işlemler gerçekleştiren bir algoritmam var
  • Şu anda, tek bir ortak arabirim yöntemi dışında tümü özel olan çok sayıda statik yönteme sahip bir modüldür.
  • Tüm yöntemler arasında paylaşılması gereken bir örnek değişkeni vardır.

Bunlar görebildiğim seçenekler, en iyisi hangisi?

  • Modül statik (Ruby 'modülünde') ile yöntemleri
  • Statik yöntemlerle sınıf
  • Veri yapısına dahil etmek için Mixin modülü
  • Refactor algoritmanın bir parçası üzerinden bu değiştirir veri yapısı (çok küçük) ve marka bu algoritma modülünün statik yöntemleri çağıran bir mixin

Teknik bölüm

Özel Modül yöntemi yapmanın herhangi bir yolu var mı ?

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

privateOrada herhangi bir etkisi görünmüyor hala çağırabilir, Thing.privsorun olmadan.


5
Bilginize Ruby'de 'statik' yöntem diye bir şey yoktur, bunlara sınıf örneği yöntemleri denir
brad

31
Eski bir yorum, ancak dört olumlu oyu olduğu için, 'sınıf örneği yöntemi' diye bir şey olmadığına dikkat etmeliyim. 'Sınıf yöntemi' doğru terimdir.
micapam

5
privatesınıf yöntemlerini değil, yalnızca örnek yöntemlerini etkiler. kullanmak private_class_methodyerine:module Thing; def self.pub; end; private_class_method :pub; end
apeiros

1
Ruby'de @micapam Sınıfı örnek yöntemleri vardır ve bunlar sınıf yöntemlerinden farklıdır.
Marnen Laibow-Koser

Yanıtlar:


88

Bence bunu yapmanın en iyi yolu (ve çoğunlukla mevcut kitaplıkların nasıl yazıldığı) modül içinde tüm mantığı ele alan bir sınıf oluşturmaktır ve modül sadece uygun bir yöntem sağlar, örn.

module GTranslate
  class Translator
    def perform( text ); translate( text ); end

    private

    def translate( text )
      # do some private stuff here
    end
  end

  def self.translate( text )
    t = Translator.new
    t.perform( text )
  end
end

14
Ruby newb burada. Bu örnekte, Translator sınıfı modülün genel arabiriminin bir parçası olarak gösteriliyor mu? 'Perform' yönteminin erişimi GTranslate ile sınırlı olabilir mi?
rsh Shepherd

2
@rsh Shepherd performBurada özel olması gereken yöntem değil, özel yöntem Translatorsınıftaki özel yöntemdir (@ ucron örneğinde hiç yok, çok talihsiz olan). GTranslate.translatesadece uygun bir yöntemdir GTranslate::Translator#perform, eğer mümkünse onu gizleyen gerçek bir kazanç yoktur.
michelpm

28
Burada ders alarak neyin başarıldığından emin değilim. Amaç özel bir modül yöntemine sahip olmaksa, bu hedefe ulaşmaz. Çünkü modülün dışından GTranslate :: Translator.new.perform'u çağırarak "perform" yöntemine erişebilirsiniz. Diğer bir deyişle, özel değildir.
Zack Xu

1
@jschorr Op ve bu cevabın bir örnek yöntemi değil, özel bir sınıf veya modül yöntemi yapmayı amaçladığını düşünüyorum. Ayrıca, self.translatebir sınıf / modül yöntemi bildirdiği için bu, herhangi bir örnek yöntemini özel yapmaz .
konsolebox

5
GTranslate::Translator.new.perform(text)- kıvrımlı, ama özel değil!
abhillman

80

Ayrıca Module.private_class_methodtartışmalı olarak daha fazla niyet ifade eden var.

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

Sorudaki kod için:

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

Ruby 2.1 veya daha yenisi:

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

Bunun farkında değildim. Yöntem tanımından önce de çalışacak privatemı?
Marnen Laibow-Koser

7
@ JCooper'ın cevabı ile birlikte bu cevap gerçek çözümdür. @ MarnenLaibow-Koser Öyle değil. Diğer yanıtı daha fazla gruplama ve girinti pahasına düşünebilirsiniz. Aslında bazıları için tercih edilen çözüm olabilir. (Sadece referans için
yanıtlıyoruz

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

4
Kabul edilen cevap bu olmalıdır. Bence temiz ve deyimsel.
Martin Nyaga

@MartinNyaga bu include Writerseçeneğin olmaması gibi bir dezavantaja sahip !
Ulysse BN

28

Bir modül karıştırıldığında süslü şeyler yapmak için "dahil edilen" yöntemi kullanabilirsiniz. Bu, sizin istediğiniz şeyi yapar.

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

5
Zekice. Yani mümkün, ama muhtemelen buna değmez.
Daniel Beardsley

güzel çalışıyor. included do |base| [...] endDef yerine kullandım
Crystark

5
@Crystark: Bu sözdizimi yalnızca, yanılmıyorsam ActiveSupport :: Concern'i genişleten modüllerde mevcuttur. yani bu bir raylı şey.
Vaz

11

Ne yazık ki, privateyalnızca örnek yöntemleri için geçerlidir. Bir sınıfta özel "statik" yöntemler edinmenin genel yolu, aşağıdaki gibi bir şey yapmaktır:

class << self
  private

  def foo()
   ....
  end
end

Kuşkusuz bunu modüller halinde yaparak oynamadım.


7
Bu doğru değil. Özel sınıf yöntemlerine ve özel modül yöntemlerine sahip olabilirsiniz.
mikeycgto

Özel sınıf yöntemlerine sahip olabilirsiniz, ancak bunu yapmak .fooözel bir sınıf yöntemi oluşturmaz: "private; def self.foo ()"
Ari

@mikeycgto Özel sınıf yöntemleri ile özel modül yöntemleri arasındaki farkı detaylandırmak ister misiniz? Çünkü bence hepsi aynı. Her iki unutmayın privateve private_class_methodaittir Moduledeğil Class. Bu kod bu arada çalışır ve kullanmaya alternatiftir private_class_method.
konsolebox

3

Güzel bir yol böyledir

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

Yöntemleri sınıf değişkenleri / sabitleri içinde lambdas olarak depolamaya ne dersiniz?

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

Test için:
UPD: 6 yıl sonra bu kodun büyük güncellemesi, özel yöntemi bildirmenin daha temiz bir yolunu gösteriyord

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

Burada şunu görüyoruz:

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) @@Ldışarıdan erişilemez, ancak hemen hemen her yerden erişilebilir
2) class << self ; private ; defyöntemi ddışarıdan ve içeriden erişilemez hale getirir self.ancak onsuz olmaz - bu tuhaftır
3) private ; self.ve private ; class << selfyöntemleri özel kılmaz - her ikisi de erişilebilirdir olan ve olmayanself.


lambdalar yöntemlerle hiç de aynı şey değildir. lambdalar türdendir, Procyöntemler ise türdendir Method.
Michael Dorst

1
genel değişkenler kötü
achempion

@achempion, onları nerede görüyorsun?
Nakilon

@Nakilon özür dilerim, oyumu iptal
etmemi

0

Özel bir modül veya sınıf oluşturun

Sabitler asla özel değildir. Bununla birlikte, bir sabite atamadan bir modül veya sınıf oluşturmak mümkündür.

Bu yüzden bir alternatif, :private_class_methodözel bir modül veya sınıf oluşturmak ve üzerinde genel yöntemler tanımlamaktır.

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

Kullanım:

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

Module.new ve Class.new dokümanlarına bakın .


Bu yöntemi gerçekten seviyorum. Ancak .self, yöntem tanımlarını kaldırmak, başka bir sınıfa dahil etmek ve bunları dahil etme sınıfının örnek_ yöntemi olarak kullanmak mümkün görünmemektedir . Çalıştırmanın bir yolu olup olmadığını biliyor musun?
Shiyason

0

Bu yöntem, verileri yöntem parametrelerine göre açıkça iletmediğiniz sürece verilerin özel yöntemlerle paylaşılmasına izin vermez.

module Thing
  extend self

  def pub
    puts priv(123)
  end

  private
  
  def priv(value)
    puts "Private method with value #{value}"
  end
end

Thing.pub
# "Private method with value 123"

Thing.priv
# NoMethodError (private method `priv' called for Thing:Module)
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.