2012-10-06 25 views
27
class Foo 
    attr_accessor :name, :age, :email, :gender, :height 

    def initalize params 
    @name = params[:name] 
    @age = params[:age] 
    @email = params[:email] 
    . 
    . 
    . 
    end 

Điều này có vẻ như một cách ngớ ngẩn khi thực hiện. Một cách tốt hơn/thành ngữ hơn của các đối tượng gây quỹ trong Ruby là gì?Làm thế nào để xóa sạch các thuộc tính trong Ruby bằng mới?

của Ruby 1.9.3

+0

Tôi thực sự nghĩ câu trả lời cho câu hỏi này là 'không' - có thể có cách tốt hơn, nhưng nếu có một cách tốt hơn và nhiều hơn nữa idiomatic_ cách sau đó sẽ có một câu trả lời rõ ràng lên bình chọn dưới đây. –

Trả lời

15
def initialize(params) 
    params.each do |key, value| 
    instance_variable_set("@#{key}", value) 
    end 
end 
+4

Tôi không thích điều này nói chung, nhưng nếu bạn định sử dụng nó, ít nhất hãy gọi người định cư thay vì đặt các biến mẫu. Nếu bạn mess tên của biến bằng cách sử dụng ivars, bạn sẽ không biết nó, nhưng bằng cách sử dụng các phương thức, bạn sẽ nhận được một NoMethodError. –

+1

@Joshua: Điều đó hoàn toàn hợp lệ. Tôi đồng ý rằng phương pháp của bạn nói chung là thích hợp hơn, tuy nhiên, tôi không phải lúc nào cũng cung cấp các setters cho tất cả các biến cá thể (ví dụ, khi chúng không thay đổi sau khi khởi tạo đối tượng). –

+1

lol, thậm chí không nhận ra tôi đã trả lời trong chủ đề này. Dù sao, tôi gần như luôn luôn truy cập thông qua setters/getters. Nếu bạn không muốn họ thay đổi, hãy tuyên bố họ là riêng tư ([ví dụ] (https://github.com/JoshCheek/seeing_is_believing/blob/3161fb906d38ebb4300333f20d84bd58fa3e7652/lib/seeing_is_believing/binary/commentable_lines.rb#L32-37)) Nhưng thực sự, mã khởi tạo như thế này chỉ nên được sử dụng trên cấu trúc dữ liệu, vì vậy vấn đề này không nên xuất hiện trong thực tế. –

36

Bạn chỉ có thể duyệt qua các phím và gọi setters. Tôi thích điều này, bởi vì nó sẽ bắt nếu bạn vượt qua một khóa không hợp lệ.

class Foo 
    attr_accessor :name, :age, :email, :gender, :height 

    def initialize params = {} 
    params.each { |key, value| send "#{key}=", value } 
    end 
end 

foo = Foo.new name: 'Josh', age: 456 
foo.name # => "Josh" 
foo.age # => 456 
foo.email # => nil 
+1

Tôi thích điều này là tốt, nhưng nó có vẻ như nó chỉ hữu ích trong các lớp học, nơi tất cả các biến dụ có setters. –

1

Nếu bạn nhận được băm làm đối số duy nhất, tại sao không chỉ giữ nguyên đó làm biến mẫu? Bất cứ khi nào bạn cần một giá trị, hãy gọi nó từ băm. Bạn có thể giữ tên biến mẫu ngắn để nó có thể dễ dàng được gọi.

class Foo 
    attr_reader :p 
    def initalize p 
    @p = p 
    end 
    def foo 
    do_something_with(@p[:name]) 
    ... 
    end 
end 

Nếu @p[:name] vẫn còn quá dài cho bạn, sau đó bạn có thể tiết kiệm một proc như là một biến Ví dụ, và gọi các giá trị liên quan như @p.(:name).

class Foo 
    attr_reader :p 
    def initialize p 
    @p = ->x{p[x]} 
    end 
    def foo 
    do_something_with(@p.(:name)) 
    ... 
    end 
end 

Hoặc, cách khác là xác định phương thức gọi hàm băm và áp dụng khóa.

class Foo 
    def initalize p 
    @p = p 
    end 
    def get key 
    @p[key] 
    end 
    def foo 
    do_something_with(get(:name)) 
    ... 
    end 
end 

Nếu muốn đặt giá trị, bạn có thể xác định phương pháp setter và kiểm tra thêm các khóa không hợp lệ nếu muốn.

class Foo 
    Keys = [:name, :age, :email, :gender, :height] 
    def initalize p 
    raise "Invalid key in argument" unless (p.keys - Keys).empty? 
    @p = p 
    end 
    def set key, value 
    raise "Invalid key" unless Keys.key?(key) 
    @p[key] = value 
    end 
    def get key 
    @p[key] 
    end 
    def foo 
    do_something_with(get(:name)) 
    ... 
    end 
end 
+0

Điều này khiến bạn có thể có các tham chiếu đi lạc: 'h = {: a =>: b}; Foo.new (h); h [: a] =: vô nghĩa'. –

3

Sử dụng tất cả các khóa từ thông số không chính xác, bạn có thể xác định tên không muốn. Tôi nghĩ rằng danh sách trắng nên có tên là

class Foo 
    @@attributes = [:name, :age, :email, :gender, :height] 

    @@attributes.each do |attr| 
    class_eval { attr_accessor "#{attr}" } 
    end 

    def initialize params 
    @@attributes.each do |attr| 
     instance_variable_set("@#{attr}", params[attr]) if params[attr] 
    end 
    end 
end 

Foo.new({:name => 'test'}).name #=> 'test' 
+0

Không xác định các thuộc tính không mong muốn - đó là một điểm tốt. –

+0

Bạn sẽ sử dụng 'get_attributes' thay vì' @@ attributes = [: name,: age,: email,: gender,: height] 'như thế nào? Chúng đã được định nghĩa với 'attr_accessor: name,: age,: email,: gender,: height'. –

+0

Bạn cũng có thể 'attr_accessor * @@ attributes'. Danh sách trắng ở đây làm cho tất cả các loại ý nghĩa. –

5

Tại sao không chỉ định rõ danh sách đối số thực tế?

class Foo 
    attr_accessor :name, :age, :email, :gender, :height 

    def initialize(name, age, email, gender, height) 
    @name = name 
    @age = age 
    @email = email 
    @gender = gender 
    @height = height 
    end 
end 

Phiên bản này có thể có nhiều dòng mã hơn những người khác, nhưng nó làm cho nó dễ dàng hơn để tận dụng (giá trị mặc định ví dụ cho các đối số hoặc lỗi nâng cao nếu initialize được gọi với arity không chính xác) được xây dựng trong các tính năng ngôn ngữ.

+3

Thông số vị trí có thể là một mối phiền toái vì nhiều lý do - khó tái cấu trúc - và quá dễ dàng để không phù hợp với các biến được truyền. –

+0

Vâng. Tôi thích được rõ ràng bất cứ nơi nào có thể, vì vậy tôi thích cách tiếp cận này. Đó là mã dễ nhất để giải thích. Tham số vị trí làm cho nó khó khăn hơn để thay đổi chữ ký sau đó, nhưng đó là một sự cân bằng tôi sẵn sàng thực hiện. –

4
Foo = Struct.new(:name, :age, :email, :gender, :height) 

Điều này là đủ cho một lớp học đầy đủ chức năng. Demo:

p Foo.class # Class 

employee = Foo.new("smith", 29, "[email protected]", "m", 1.75) #create an instance 
p employee.class # Foo 
p employee.methods.sort # huge list which includes name, name=, age, age= etc 
+1

Lưu ý rằng điều này không chơi tốt kết hợp với 'include ActiveModel :: Model'. –

9

Để tận dụng câu trả lời Joshua Cheek với một chút khái quát

module Initializable 
    def initialize(params = {}) 
    params.each do |key, value| 
     setter = "#{key}=" 
     send(setter, value) if respond_to?(setter.to_sym, false) 
    end 
    end 
end 

class Foo 
    include Initializable 

    attr_accessor :name, :age, :email, :gender, :height 
end 

Foo.new name: 'Josh', age: 456 
=> #<Foo:0x007fdeac02ecb0 @name="Josh", @age=456> 

NB Nếu khởi mix-in đã được sử dụng chúng ta cần khởi tạo tùy chỉnh, chúng tôi chỉ cần gọi super:

class Foo 
    include Initializable 

    attr_accessor :name, :age, :email, :gender, :height, :handler 

    def initialize(*) 
    super 

    self.handler = "#{self.name} #{self.age}" 
    end 
end 

Foo.new name: 'Josh', age: 45 
=> #<Foo:0x007fe94c0446f0 @name="Josh", @age=45, @handler="Josh 45">