2009-02-23 25 views
9

Tôi có một lớp học mà nên tìm kiếm một cái gì đó như thế này:động tạo lớp trong Ruby

class Family_Type1 
    @people = Array.new(3) 
    @people[0] = Policeman.new('Peter', 0) 
    @people[1] = Accountant.new('Paul', 0) 
    @people[2] = Policeman.new('Mary', 0) 

    def initialize(*ages) 
     for i in 0 ... @people.length 
      @people[i].age = ages[i] 
     end 
    end 
end 

Tôi muốn để có thể xác định một loạt các lớp học tương tự với trang này trong thời gian chạy (định nghĩa chúng một lần lúc khởi động) trong đó kích thước của mảng và kiểu được gán cho mỗi tham số được định nghĩa trong thời gian chạy từ một tệp đặc tả bên ngoài.

Tôi sắp xếp để nó hoạt động bằng cách sử dụng evals nhưng điều này thực sự xấu xí. Có cách nào tốt hơn không?

Trả lời

9

Trước hết, một phần lý do mã ví dụ của bạn không làm việc cho bạn là bạn có hai @people biến khác nhau - một là một dụ biến và người kia là một trường hợp lớp biến.

class Example 
    # we're in the context of the Example class, so 
    # instance variables used here belong to the actual class object, 
    # not instances of that class 
    self.class #=> Class 
    self == Example #=> true 
    @iv = "I'm a class instance variable" 

    def initialize 
    # within instance methods, we're in the context 
    # of an _instance_ of the Example class, so 
    # instance variables used here belong to that instance. 
    self.class #=> Example 
    self == Example #=> false 
    @iv = "I'm an instance variable" 
    end 
    def iv 
    # another instance method uses the context of the instance 
    @iv #=> "I'm an instance variable" 
    end 
    def self.iv 
    # a class method, uses the context of the class 
    @iv #=> "I'm a class instance variable" 
    end 
end 

Nếu bạn muốn tạo biến một lần trong một lớp học để sử dụng trong các phương pháp thể hiện của lớp đó, sử dụng constants hoặc class variables.

class Example 
    # ruby constants start with a capital letter. Ruby prints warnings if you 
    # try to assign a different object to an already-defined constant 
    CONSTANT_VARIABLE = "i'm a constant" 
    # though it's legit to modify the current object 
    CONSTANT_VARIABLE.capitalize! 
    CONSTANT_VARIABLE #=> "I'm a constant" 

    # class variables start with a @@ 
    @@class_variable = "I'm a class variable" 

    def c_and_c 
    [ @@class_variable, CONSTANT_VARIABLE ] #=> [ "I'm a class variable", "I'm a constant" ] 
    end 
end 

Mặc dù vậy, trong ngữ cảnh của bạn, bạn có thể không muốn tất cả các trường hợp của bạn Family_Type1 đề cập đến cùng một cảnh sát và kế toán phải không? Hay bạn?

Nếu chúng ta chuyển sang sử dụng các biến lớp:

class Family_Type1 
    # since we're initializing @@people one time, that means 
    # all the Family_Type1 objects will share the same people 
    @@people = [ Policeman.new('Peter', 0), Accountant.new('Paul', 0), Policeman.new('Mary', 0) ] 

    def initialize(*ages) 
     @@people.zip(ages).each { |person, age| person.age = age } 
    end 
    # just an accessor method 
    def [](person_index) 
     @@people[person_index] 
    end 
end 
fam = Family_Type1.new(12, 13, 14) 
fam[0].age == 12 #=> true 
# this can lead to unexpected side-effects 
fam2 = Family_Type1.new(31, 32, 29) 
fam[0].age == 12 #=> false 
fam2[0].age == 31 #=> true 
fam[0].age == 31 #=> true 

Việc khởi chạy, có thể thực hiện với lập trình meta, như Chirantan nói, nhưng nếu bạn chỉ khởi tạo một vài lớp học, và bạn biết những gì tên của họ là, bạn cũng có thể làm điều đó chỉ bằng cách sử dụng bất cứ điều gì bạn đọc từ tệp:

PARAMS = File.read('params.csv').split("\n").map { |line| line.split(',') } 
make_people = proc do |klasses, params| 
    klasses.zip(params).map { |klass,name| klass.new(name, 0) } 
end 
class Example0 
    @@people = make_people([ Fireman, Accountant, Fireman ], PARAMS[0]) 
end 
class Example1 
    @@people = make_people([ Butcher, Baker, Candlestickmaker ], PARAMS[0]) 
end 
+0

Đây là câu trả lời duy nhất thực sự giải thích toàn bộ tình huống IMO. – Chuck

+0

giải thích tuyệt vời, giúp xóa các khái niệm ... thnks –

1

Giả sử bạn muốn tạo lớp khác nhau cho mỗi kích thước loại/mảng khi chạy:

Nếu (như trong Python) một lớp học của Ruby được định nghĩa khi được thực thi (Tôi nghĩ rằng đó là), sau đó bạn có thể làm điều này :

Xác định lớp học của bạn bên trong một hàm. Có chức năng nhận kích thước mảng và nhập làm tham số và trả về lớp trong kết quả của nó. Bằng cách đó, bạn có một loại nhà máy lớp để gọi cho từng định nghĩa trong tệp spec của bạn :)

Nếu mặt khác bạn muốn chỉ khởi tạo @params dựa trên dữ liệu thực tế, hãy nhớ rằng Ruby là động ngôn ngữ đã nhập: Chỉ cần gán lại @params trong hàm tạo của bạn cho mảng mới!

+0

Cảm ơn - đã cho đến nay với việc tạo bên trong một hàm, nhưng câu hỏi liên quan đến việc triển khai - ví dụ thêm/khởi tạo mảng bên ngoài phương thức 'khởi tạo', v.v. –

+0

Khi khởi tạo, tôi chỉ muốn gửi giá trị - không phải là đối tượng hoàn chỉnh. Cho phép nói biến cá thể là một mảng của đối tượng Person derived, sau đó tôi muốn mảng với các kiểu là một phần của định nghĩa lớp, và trong khởi tạo, tôi sẽ chỉ chuyển tên của mỗi người. –

32

Từ những gì tôi hiểu, bạn cần lập trình meta. Dưới đây là một đoạn mã để tạo các lớp động (khi đang chạy) với phương thức khởi tạo khởi tạo các biến mẫu -

class_name = 'foo'.capitalize 
klass = Object.const_set(class_name,Class.new) 

names = ['instance1', 'instance2'] # Array of instance vars 

klass.class_eval do 
    attr_accessor *names 

    define_method(:initialize) do |*values| 
    names.each_with_index do |name,i| 
     instance_variable_set("@"+name, values[i]) 
    end 
    end 
    # more... 
end 

Hy vọng bạn có thể tinh chỉnh nó cho phù hợp với yêu cầu của bạn.

+0

Cảm ơn, có vẻ phức tạp hơn khi biến mẫu là một mảng các đối tượng, và phương thức khởi tạo chỉ nên đặt một trường nhất định trong mỗi đối tượng. –

+0

Dude, trong trường hợp của bạn, bạn có thể làm instance_variable_set ("@ params [# {i}]. Age", age [i]) và bạn đã xong ... Thats cách bạn có thể xử lý các mảng. Bạn cần phải đào sâu vào nó một chút để hiểu nó. – Chirantan

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