Làm thế nào để tạo một phương thức lớp riêng?


216

Cách tiếp cận này của việc tạo một phương thức lớp riêng hoạt động như thế nào:

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

Nhưng điều này không:

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
Tôi chỉ thấy bài viết này thảo luận về các cách để tạo ra các phương thức lớp riêng và nghĩ rằng nó là tốt: jakeyesbeck.com/2016/01/24/ruby-private- class
Long Long

Câu trả lời:


265

privatedường như không hoạt động nếu bạn đang xác định một phương thức trên một đối tượng rõ ràng (trong trường hợp của bạn self). Bạn có thể sử dụng private_class_methodđể định nghĩa các phương thức lớp là riêng tư (hoặc giống như bạn đã mô tả).

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

Ngoài ra (trong ruby ​​2.1+), do định nghĩa phương thức trả về ký hiệu của tên phương thức, bạn cũng có thể sử dụng cách này như sau:

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 đã viết:

Hành vi như vậy của ruby ​​thực sự là bực bội. Ý tôi là nếu bạn chuyển sang phần riêng tư self.method thì nó KHÔNG riêng tư. Nhưng nếu bạn di chuyển nó đến lớp << thì nó đột nhiên hoạt động. Nó thật kinh tởm

Có thể nhầm lẫn nó có thể là, bực bội nó có thể được, nhưng ghê tởm nó chắc chắn là không.

Nó làm cho cảm giác hoàn hảo khi bạn hiểu mô hình đối tượng Ruby và tương ứng với phương pháp tra cứu chảy , đặc biệt là khi tính đến đó privateKHÔNG một modifier truy cập / tầm nhìn, nhưng thực sự là một lời gọi phương thức (với lớp như nhận nó) như đã thảo luận ở đây ... không có thứ gọi là "phần riêng tư" trong Ruby.

Để xác định các phương thức cá thể riêng tư , bạn gọi privatelớp của cá thể để đặt mức độ hiển thị mặc định cho các phương thức được xác định sau đó thành riêng tư ... và do đó, sẽ rất hợp lý khi định nghĩa các phương thức lớp riêng bằng cách gọi privatelớp của lớp, tức là. siêu dữ liệu của nó.

Các ngôn ngữ OO chính thống, tự xưng khác có thể cung cấp cho bạn một cú pháp ít gây nhầm lẫn hơn, nhưng bạn chắc chắn đánh đổi điều đó với một mô hình đối tượng khó hiểu và ít nhất quán (không nhất quán?) Mà không có sức mạnh của các phương tiện siêu lập trình của Ruby.


Vì vậy, nếu tôi hiểu chính xác, bản thân ruby ​​không có từ khóa sửa đổi truy cập (công khai, riêng tư và được bảo vệ) mà có các phương thức sửa đổi truy cập (công khai, riêng tư, được bảo vệ)? Đây có phải là thứ cần được đưa lên trình theo dõi lỗi ruby ​​để Matz thực hiện các sửa đổi truy cập từ khóa thích hợp hay đây là hành vi được mong đợi?
Edward

13
@Edward Nó được thiết kế theo cách đó Junichiito.blogspot.co.uk/2012/03/ . Tại sao "thích hợp"?
iain

1
Tiếp theo từ điều này, thay vì làm private_class_method :method_namebạn có thể làm private_class_method def method_name....
bjt38

send(private_method)cũng có thể truy cập bên ngoài đối tượng.
stevenspiel

1
@ bjt38 Chỉ cần rõ ràng, nó sẽ làprivate_class_method def self.method_name
Tom

77

Theo mặc định, tất cả các phương thức lớp là công khai. Để đặt chúng ở chế độ riêng tư, bạn có thể sử dụng Mô-đun # private_group_method như @tjwallace đã viết hoặc định nghĩa chúng khác nhau, như bạn đã làm:

class << self

  private

  def method_name
    ...
  end
end

class << selfmở lớp singleton tự, để các phương thức có thể được định nghĩa lại cho đối tượng tự hiện tại. Điều này được sử dụng để định nghĩa phương thức lớp / mô-đun ("tĩnh"). Chỉ có ở đó, việc xác định các phương thức riêng thực sự mang lại cho bạn các phương thức lớp riêng.


17

Để hoàn thiện, chúng tôi cũng có thể tránh khai báo private_group_method trong một dòng riêng biệt. Cá nhân tôi không thích cách sử dụng này nhưng thật tốt khi biết rằng nó tồn tại.

private_class_method  def self.method_name
 ....
end

5

Tôi cũng vậy, tìm thấy Ruby (hoặc ít nhất là kiến ​​thức của tôi về nó) thiếu dấu hiệu trong lĩnh vực này. Ví dụ, những điều sau đây làm những gì tôi muốn nhưng vụng về,

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

Vấn đề của tôi với mã ở trên là các yêu cầu cú pháp của Ruby và các số liệu về chất lượng mã của tôi được thực hiện cho mã rườm rà. Để có mã cả hai hoạt động như tôi muốn và để làm im lặng các số liệu, tôi phải đặt so sánh () một phương thức lớp. Vì tôi không muốn nó là một phần của API công khai của lớp, tôi cần nó ở chế độ riêng tư, nhưng 'riêng tư' không hoạt động. Thay vào đó, tôi buộc phải sử dụng 'private_group_method' hoặc một số công việc như vậy. Đến lượt mình, điều này buộc phải sử dụng 'self. Class.send (: so sánh ...' cho mỗi biến tôi kiểm tra trong '== ()'. Bây giờ điều đó hơi khó sử dụng.


Thực tế là bạn cần sử dụng gửi không liên quan gì đến "cách" bạn đánh dấu các phương thức lớp riêng tư. Các phương thức riêng tư không thể được gọi từ "bên ngoài".
Pascal

4

Các phương thức sơ thẩm được định nghĩa bên trong một khối định nghĩa lớp. Các phương thức lớp được định nghĩa là các phương thức singleton trên lớp singleton của một lớp, còn được gọi một cách không chính thức là "metaclass" hoặc "eigenclass". privatekhông phải là một từ khóa, mà là một phương thức ( Module # private ).

Đây là một cuộc gọi đến phương thức self#private/ A#privatemà "bật tắt" quyền truy cập riêng cho tất cả các định nghĩa phương thức cá thể sắp tới cho đến khi được bật khác:

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

Như đã lưu ý trước đó, các phương thức lớp thực sự là các phương thức singleton được định nghĩa trên lớp singleton.

def A.class_method; end

Hoặc sử dụng một cú pháp đặc biệt để mở phần định nghĩa của lớp A, ẩn danh của A:

class << A
  def class_method; end
end

Người nhận "tin nhắn riêng" - tự - bên trong class Alà đối tượng lớp A. tự bên trong class << Akhối là một đối tượng khác, lớp đơn.

Ví dụ sau đây trong thực tế gọi hai phương thức khác nhau được gọi là riêng tư , sử dụng hai người nhận hoặc mục tiêu khác nhau cho cuộc gọi. Trong phần đầu tiên, chúng ta định nghĩa một phương thức cá thể riêng ("trên lớp A"), trong phần sau chúng ta định nghĩa một phương thức lớp riêng (trên thực tế là một phương thức singleton trên đối tượng lớp singleton củ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

Bây giờ, viết lại ví dụ này một chút:

class A
  private
    def self.class_method; end
end

Bạn có thể thấy lỗi [mà các nhà thiết kế ngôn ngữ Ruby] mắc phải không? Bạn chuyển đổi quyền truy cập riêng cho tất cả các phương thức cá thể sắp tới của A, nhưng tiến hành khai báo một phương thức singleton trên một lớp khác, lớp singleton.


-1

Ruby dường như cung cấp một giải pháp kém. Để giải thích, hãy bắt đầu với một ví dụ C ++ đơn giản cho thấy quyền truy cập vào các phương thức lớp riêng:

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

Chạy ở trên

   % ./a.out
   instance method
   class method

Bây giờ Ruby dường như không cung cấp tương đương. Các quy tắc của Ruby, tôi nghĩ, là các phương thức riêng tư không được truy cập bằng máy thu. Đó là,

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

Điều đó ổn đối với các phương thức cá nhân, nhưng gây ra vấn đề với các phương thức lớp riêng.

Tôi muốn Ruby hoạt động theo cách này:

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

Nhưng, than ôi, những điều trên không hoạt động. Có ai biết một cách tốt hơn?

Khi tôi thấy 'gửi' trước một phương thức, đó là một dấu hiệu rõ ràng mã đang vi phạm mục đích của người thiết kế API, nhưng trong trường hợp này, thiết kế đặc biệt có một phương thức cá thể của lớp gọi phương thức lớp riêng.


-14

Kể từ ruby ​​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

Tôi đã thử điều này private def self.second_methodtrước mỗi ký hiệu phương pháp, không hoạt động trên viên ruby ​​của tôi. Nhưng ký hiệu này làm việc cho tôi.
Emile Vrijdags

11
Điều này là không chính xác, bởi vì cuộc gọi Check.second_methodcũng sẽ hoạt động mà không có vấn đề gì, vì vậy nó không thực sự riêng tư.
Deiwin

1
Nó sẽ không hoạt động thử điều nàyprivate_class_method :second_method
KING SABRI
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.