2013-08-10 53 views
6

Tôi mới sử dụng Ruby và đến từ thế giới C#. Trong C# nó là hợp pháp để làm những việc như thế này:Gọi phương thức lớp riêng từ phương thức cá nhân

public class Test 
{ 
    public void Method() 
    { 
    PrivateMethod(); 
    } 

    private void PrivateMethod() 
    { 
    PrivateStaticMethod(); 
    } 

    private static void PrivateStaticMethod() 
    { 
    } 
} 

Có thể làm điều gì đó tương tự trong Ruby không?

Một chút ngữ cảnh: Tôi có ứng dụng Rails ... Một trong các mô hình có phương pháp riêng tư thiết lập một số phụ thuộc. Có một phương thức lớp tạo ra cá thể khởi tạo của mô hình. Vì lý do kế thừa, có một số trường hợp của mô hình không được khởi tạo chính xác. Tôi đã thêm một phương thức thể hiện khởi tạo các trường hợp 'uninitialized' mà tôi muốn thực hiện cùng một logic khởi tạo. Có cách nào để tránh trùng lặp không?

mẫu:

class MyModel < ActiveRecord::Base 

    def self.create_instance 
    model = MyModel.new 
    model.init_some_dependencies # this fails 
    model 
    end 

    def initialize_instance 
    // do some other work 
    other_init 
    // call private method 
    init_some_dependencies 
    end 

    private 

    def init_some_dependencies 
    end 

end 

tôi đã cố gắng để chuyển đổi phương pháp riêng của tôi để một phương pháp học riêng, nhưng tôi vẫn nhận được một lỗi:

class MyModel < ActiveRecord::Base 

    def self.create_instance 
    model = MyModel.new 
    MyModel.init_some_dependencies_class(model) 
    model 
    end 

    def initialize_instance 
    # do some other work 
    other_init 
    # call private method 
    init_some_dependencies 
    end 

    private 

    def init_some_dependencies 
    MyModel.init_some_dependencies_class(self) # now this fails with exception 
    end 

    def self.init_some_dependencies_class(model) 
    # do something with model 
    end 

    private_class_method :init_some_dependencies_class 

end 

Trả lời

4

Trước tiên cho tôi cố gắng giải thích lý do tại sao mã không hoạt động

class MyModel < ActiveRecord::Base 

    def self.create_instance 
    model = MyModel.new 
    # in here, you are not inside of the instance scope, you are outside of the object 
    # so calling model.somemething can only access public method of the object. 
    model.init_some_dependencies 
    ... 
    end 
    ... 

Bạn có thể bỏ qua sự kêu gọi riêng của phương pháp này với model.send :init_some_dependencies. Nhưng tôi nghĩ trong trường hợp này có lẽ là giải pháp tốt hơn.

Tôi đoán rằng init_some_dependencies có thể chứa nhiều logic kinh doanh/miền hơn là kiên trì. Đó là lý do tại sao tôi sẽ đề nghị để đưa ra logic này vào một "Domain Object" (hoặc một số gọi nó là Object Service). Mà chỉ là một đối tượng ruby ​​đơn giản có chứa logic miền.

Bằng cách này bạn có thể tách logic kiên trì thành ActiveRecord và logic miền cho lớp đó. Do đó bạn sẽ không làm nổi bật mô hình ActiveRecord. Và bạn nhận được tiền thưởng thử nghiệm logic miền mà không cần ActiveRecord. Điều này sẽ làm cho bài kiểm tra của bạn nhanh hơn.

Bạn có thể tạo tệp có nội dung `lib/MyModelDomain.rb'

class MyModelDomain 
    attr_accessor :my_model 

    def initialize(my_model) 
    @my_model = my_model  
    end 

    def init_some_dependencies 
    my_model.property = 'some value example' 
    end 
end 

Bây giờ bạn có thể sử dụng đối tượng này nói điều gì đó như thế này

class MyModel < ActiveRecord::Base 

    def self.create_instance 
    model = MyModel.new 
    domain = MyModelDomain.new(model) 
    domain.init_some_dependencies 
    domain.my_model 
    end 

    def initialize_instance 
    // do some other work 
    other_init 

    domain = MyModelDomain.new(self) 
    domain.init_some_dependencies 
    end 
end 

Bạn cũng có thể muốn di chuyển initialize_instance nếu bạn nghĩ rằng nó là cần thiết

Một số tài nguyên mà đi sâu vào này mẫu:

-3

acturally, nó chắc chắn không.

Một số chiến lược OO của Ruby (private & public từ khóa, v.v.) đến từ C++, vì vậy bạn có thể sử dụng gần như giống nhau.

+0

Hãy xem một số tài liệu cho điều đó. –

+2

Tôi không biết matz có thể nhìn thấy tương lai! (Gợi ý: Ruby xuất hiện gần một thập kỷ trước khi C#.) Mô hình OO của Ruby được lấy cảm hứng từ Smalltalk, khá xa cả C++ và C# hết mức có thể. –

+0

@ JörgWMittag Ý tôi là, điều quan trọng ở đây không phải là mô hình đối tượng của Ruby từ (của couse tôi biết đó là Smalltalk). Ruby cũng nhận được lợi ích từ chiến lược oo của C++ như các từ khóa 'private' và' public'. –

1

Bạn có thể sử dụng

model = MyModel.new 
model.send :init_some_dependencies 

để bỏ qua kiểm tra tầm nhìn phương pháp.

-1

In C# it is legal to do stuff like this:

public class Test 
{ 
    public void Method() 
    { 
    PrivateMethod(); 
    } 

    private void PrivateMethod() 
    { 
    PrivateStaticMethod(); 
    } 

    private static void PrivateStaticMethod() 
    { 
    } 
} 

Is it possible to do something similar in Ruby?

Có:

class Test 

    def method 
    private_method() 
    end 


    def self.greet 
    puts 'Hi' 
    end 

    private_class_method :greet 


    private 

    def private_method 
    self.class.class_eval do 
     greet 
    end 
    end 

end 

Test.new.method 
Test.greet 

--output:-- 
Hi 
1.rb:23:in `<main>': private method `greet' called for Test:Class (NoMethodError) 

Nhưng ruby ​​không thi hành nghiêm chỉnh sự riêng tư. Ví dụ,

class Dog 
    def initialize 
    @private = "secret password" 
    end 
end 

puts Dog.new.instance_variable_get(:@private) 

--output:-- 
secret password 

ruby ​​mang đến cho bạn sự tự do để truy cập vào những thứ riêng với một chút nỗ lực nhiều:

Test.new.method 

Test.class_eval do 
    greet 
end 

--output:-- 
Hi 
Hi 

Trong ruby, một phương pháp tư nhân chỉ có nghĩa rằng bạn không thể chỉ định rõ ràng một mạch thu phương pháp, nghĩa là không thể có tên và dấu chấm ở bên trái của phương thức. Nhưng trong ruby, một phương pháp mà không có người nhận ngầm sử dụng tự như người nhận. Vì vậy, để gọi một phương thức riêng, bạn chỉ cần tạo ra một bối cảnh trong đó bản thân là người nhận chính xác. Cả class_eval và instance_eval đều tự thay đổi bên trong khối thành bộ thu của chúng, ví dụ:

some_obj.instance_eval do 
    #Inside here, self=some_obj 

    #Go crazy and call private methods defined in some_obj's class here 

end 

Bạn có thể áp dụng những quy tắc để tình trạng này:

(ahmy wrote:) 

First let me try to explain why the code does not work 

    class MyModel < ActiveRecord::Base 

     def self.create_instance 
     model = MyModel.new 
     # in here, you are not inside of the instance scope, you are outside of the object 
     # so calling model.somemething can only access public method of the object. 
     model.init_some_dependencies # this fails 
     ... end ... 

"Bối cảnh này" và "phạm vi mà" - những gì đau đầu. Tất cả những gì bạn phải nhớ là: bạn không thể gọi một phương thức riêng với người nhận rõ ràng. Phương thức init_some_dependencies được định nghĩa là phương thức riêng - nhưng nó có "mô hình". được viết ở bên trái của nó. Đó là một người nhận rõ ràng. Bang! Một lỗi.

Đây là một giải pháp:

class MyModel 

    def self.create_instance 
    #In here, self=MyModel 
    puts self 

    model = MyModel.new 

    model.instance_eval do #Changes self to model inside the block 
     #In here, self=model 
     init_some_dependencies #Implicitly uses self as the receiver, so that line is equivalent to model.init_some_dependencies 
    end 

    end 


    private 

    def init_some_dependencies 
    puts "Dependencies have been initialized!" 
    end 
end 

MyModel.create_instance 

--output:-- 
MyModel 
Dependencies have been initialized! 

Hoặc như ahmy và LBG chỉ ra, bạn có thể sử dụng Object # send() để gọi các phương thức riêng:

class MyModel 

    def self.create_instance 
    model = MyModel.new 
    model.send(:init_some_dependencies, 10, 20) 
    end 


    private 

    def init_some_dependencies(*args) 
    puts "Dependencies have been initialized with: #{args}!" 
    end 
end 


MyModel.create_instance 

--output:-- 
Dependencies have been initialized with: [10, 20]! 
+0

C# không thực thi quyền riêng tư, bạn có thể truy cập riêng tư trong C# cũng như thông qua phản ánh. – Esailija

+0

@Esailija, tôi không biết gì về C#. Đó là một câu hỏi ruby. – 7stud

Các vấn đề liên quan