Các phương thức mô-đun riêng trong Ruby


108

Tôi có một câu hỏi hai phần

Thực hành tốt nhất

  • Tôi có một thuật toán thực hiện một số thao tác trên cấu trúc dữ liệu bằng giao diện công khai
  • Nó hiện là một mô-đun có nhiều phương thức tĩnh, tất cả đều riêng tư ngoại trừ một phương thức giao diện công khai.
  • Có một biến thể hiện cần được chia sẻ giữa tất cả các phương thức.

Đây là những tùy chọn tôi có thể thấy, tùy chọn nào tốt nhất ?:

  • Mô-đun với phương thức tĩnh ('mô-đun' trong ruby)
  • Lớp với các phương thức tĩnh
  • Mô-đun Mixin để đưa vào cấu trúc dữ liệu
  • Cấu trúc lại một phần của thuật toán sửa đổi cấu trúc dữ liệu đó (rất nhỏ) và tạo ra một mixin gọi các phương thức tĩnh của mô-đun thuật toán

Phần kỹ thuật

Có cách nào để tạo một phương thức Module riêng không?

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

Trong privateđó dường như không có bất kỳ tác dụng nào , tôi vẫn có thể gọi Thing.privmà không gặp sự cố.


5
FYI không có những điều như một phương pháp 'tĩnh' trong ruby, chúng được gọi là phương pháp lớp dụ
brad

31
Một nhận xét cũ, nhưng vì nó có bốn phiếu tán thành, tôi phải chỉ ra rằng không có cái gọi là 'phương thức cá thể lớp'. 'Phương thức lớp' là thuật ngữ chính xác.
micapam

5
privatechỉ ảnh hưởng đến các phương thức thể hiện, không ảnh hưởng đến các phương thức lớp. sử dụng private_class_methodthay thế:module Thing; def self.pub; end; private_class_method :pub; end
apeiros

1
@micapam Các phương thức cá thể của lớp tồn tại trong Ruby và chúng khác với các phương thức của lớp.
Marnen Laibow-Koser

Câu trả lời:


88

Tôi nghĩ rằng cách tốt nhất (và chủ yếu là cách các lib hiện có được viết) để làm điều này là tạo một lớp bên trong mô-đun xử lý tất cả các logic và mô-đun chỉ cung cấp một phương thức thuận tiện, ví dụ:

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 đây. Trong ví dụ này, lớp Translator có được hiển thị như một phần của giao diện công khai của mô-đun không? Phương thức 'performance' có thể bị hạn chế quyền truy cập vào GTranslate không?
rshepherd vào

2
@rshepherd Ở performđây không phải là phương thức được cho là private, phương thức private là phương thức private trong Translatorlớp (ví dụ của @ ucron không có cái nào, điều rất đáng tiếc). GTranslate.translatechỉ là một phương pháp thuận tiện GTranslate::Translator#perform, không có lợi ích thực sự nào che giấu nó, nếu nó hoàn toàn có thể.
michelpm

28
Tôi không chắc những gì đạt được khi có một lớp học ở đây. Nếu mục tiêu là có một phương thức mô-đun riêng thì điều này không đáp ứng được mục tiêu. Bởi vì bạn có thể truy cập phương thức "thực hiện" từ bên ngoài mô-đun bằng cách gọi GTranslate :: Translator.new.perform. Nói cách khác, nó không phải là riêng tư.
Zack Xu

1
@jschorr Tôi nghĩ Op và câu trả lời này có ý định tạo một phương thức mô-đun hoặc lớp riêng, không phải là một phương thức cá thể. Ngoài ra, điều đó sẽ không làm cho bất kỳ phương thức cá thể nào là riêng tư như self.translatekhai báo phương thức lớp / mô-đun.
konsolebox

5
GTranslate::Translator.new.perform(text)- phức tạp, nhưng không riêng tư!
abhillman

80

Ngoài ra còn có Module.private_class_method, được cho là thể hiện nhiều ý định hơn.

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

Đối với mã trong câu hỏi:

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

Ruby 2.1 hoặc mới hơn:

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

Tôi không biết về điều này. Nó sẽ hoạt động trước cả định nghĩa phương thức, như thế privatenào?
Marnen Laibow-Koser

7
Câu trả lời này cùng với câu trả lời của @ JCooper là giải pháp thực sự. @ MarnenLaibow-Koser Nó không. Bạn có thể xem xét câu trả lời khác với chi phí nhóm và thụt lề nhiều hơn. Nó thực sự có thể là giải pháp ưa thích cho một số. (Trả lời chỉ vì lợi ích của tài liệu tham khảo.)
konsolebox

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
Đây phải là câu trả lời được chấp nhận. Sạch sẽ và thành ngữ theo quan điểm của tôi.
Martin Nyaga

@MartinNyaga cái này có nhược điểm là không có include Writertùy chọn!
Ulysse BN

28

Bạn có thể sử dụng phương thức "bao gồm" để thực hiện những điều thú vị khi một mô-đun được trộn vào. Điều này thực hiện theo những gì bạn muốn Tôi nghĩ:

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
Thật khéo léo. Vì vậy, nó có thể, nhưng có lẽ là không đáng.
Daniel Beardsley

hoạt động tốt. Tôi sử dụng included do |base| [...] endthay vì def
Crystark

5
@Crystark: Cú pháp đó chỉ tồn tại trên các mô-đun mở rộng ActiveSupport :: Quan tâm nếu tôi không nhầm. tức là nó là một thứ đường ray.
Vaz

11

Thật không may, privatechỉ áp dụng cho các phương thức cá thể. Cách chung để nhận các phương thức "tĩnh" riêng tư trong một lớp là thực hiện một số việc như:

class << self
  private

  def foo()
   ....
  end
end

Phải thừa nhận rằng tôi chưa chơi với việc làm điều này trong các mô-đun.


7
Đây không phải là sự thật. Bạn có thể có phương thức lớp riêng và phương thức mô-đun riêng.
mikeycgto

Bạn có thể có các phương thức lớp riêng, nhưng chỉ làm điều này sẽ không tạo ra .foomột phương thức lớp riêng: "private; def self.foo ()"
Ari

@mikeycgto Bạn muốn giải thích sự khác biệt giữa phương thức lớp riêng và phương thức mô-đun riêng? Vì tôi nghĩ chúng giống nhau. Lưu ý rằng cả hai privateprivate_class_methodthuộc sở hữu của Modulekhông Class. Mã này hoạt động theo cách này và nó là sự thay thế để sử dụng private_class_method.
konsolebox

3

Một cách tốt đẹp là như thế này

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

Còn về việc lưu trữ các phương thức dưới dạng lambdas bên trong các biến / hằng số của lớp thì sao?

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

Để thử nghiệm:
UPD: bản cập nhật lớn của mã này sau 6 năm cho thấy cách rõ ràng hơn để khai báo phương thức riêng tư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

Ở đây chúng ta thấy rằng:

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) @@Lkhông thể truy cập từ bên ngoài nhưng có thể truy cập từ hầu hết mọi nơi
2) class << self ; private ; deflàm cho phương thức dkhông thể truy cập thành công từ bên ngoài và từ bên trong self.nhưng không phải là không có nó - điều này thật kỳ lạ
3) private ; self.private ; class << selfkhông đặt phương thức ở chế độ riêng tư - chúng đều có thể truy cập được có và không cóself.


lambdas hoàn toàn không giống với các phương thức. lambdas là loại Proc, trong khi phương thức là loại Method.
Michael Dorst

1
các biến số toàn cầu là xấu
achearies

@acheosystem, bạn thấy họ ở đâu?
Nakilon

@Nakilon lời xin lỗi của tôi, chỉnh sửa câu trả lời của bạn nếu bạn muốn mà tôi hủy bỏ lá phiếu của tôi
achempion

0

Tạo một mô-đun hoặc lớp học riêng tư

Hằng số không bao giờ là riêng tư. Tuy nhiên, có thể tạo một mô-đun hoặc lớp mà không cần gán nó cho một hằng số.

Vì vậy, một giải pháp thay thế :private_class_methodlà tạo một mô-đun hoặc lớp riêng và xác định các phương thức công khai trên đó.

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

Sử dụng:

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

Xem tài liệu cho Module.newClass.new .


Tôi thực sự thích phương pháp này. Nhưng dường như không thể loại bỏ các .selfđịnh nghĩa trong phương thức, đưa nó vào một lớp khác và sử dụng chúng như các instance_methods của lớp bao gồm. Bạn có biết nếu có bất kỳ cách để làm cho nó hoạt động?
Shiyason

0

Phương thức này sẽ không cho phép chia sẻ dữ liệu với các phương thức private trừ khi bạn chuyển dữ liệu một cách rõ ràng bằng các tham số phương thức.

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.