Избегать повторений при определении класса в Ruby?
-
14-12-2019 - |
Вопрос
Я обнаруживаю, что часто повторяюсь, когда создаю классы в ruby, часто в итоге у меня получается что-то похожее на следующее:
class Foo
attr_reader :bar_0,
:bar_1,
.
.
.
:bar_n
def initialize( bar_0 = something,
bar_1 = something,
.
.
.
bar_n = something)
@bar_0 = bar_0
@bar_1 = bar_1
.
.
.
@bar_n = bar_n
end
end
Использует ли ruby ярлык для более эффективной реализации чего-то подобного?
Решение
Судя по тому, как сформулирован вопрос, вам, вероятно, следует переосмыслить дизайн ваших классов.Однако Ruby предоставляет интересный способ быстрого создания классов с помощью attr_accessor
субъекты (не читатели).Вот простой пример:
>> class Person < Struct.new(:name, :age) ; end
=> nil
>> p = Person.new
=> #<struct Person name=nil, age=nil>
>> p.age = 23
=> 23
>> p.class
=> Person
>> p.methods.grep(/age/)
=> [:age, :age=]
Конечно, это обычный класс, и вы можете добавить все методы, которые хотите (и использовать getters и setters вместо переменных экземпляра, например var
для добытчика и self.var = foo
для сеттера).
Если вам действительно не нужны авторы, сделайте их частными или undef
их.
>> attrs = [:name, :age]
=> [:name, :age]
>> class Person < Struct.new *attrs ; end
=> nil
>> Person.instance_eval { private *attrs.map{|attr| "#{attr}=" }}
=> Person
>> p = Person.new
=> #<struct Person name=nil, age=nil>
>> p.methods.grep(/age/)
=> [:age]
Все вышеперечисленное не помогает с тоннами заданий в initialize
конечно, но тогда возникает вопрос, действительно ли вам нужно много аргументов конструктора или, возможно, у вас есть только один хэш-аргумент и вы объединяете его в хэш по умолчанию.
Другие советы
Ruby Dynamic и предлагает много с точки зрения самоанализ, поэтому вы можете использовать MetaProgramming (или код записи, который по существу записывает код).В вашем примере есть несколько вещей, которые вы можете сделать, чтобы убрать полкость:
class Foo
# Rather than writing bar_1, bar_2, bar_3, ...
attr_accessor ((0..9).to_a + ('a'..'n').to_a).map { |x| :"foo_#{x}" }
# Using mass assignment...
def initialize(attributes = {})
attributes.each do |attribute, value|
respond_to?(:"#{attribute}=") && send(:"#{attribute}=", value)
end
end
end
.
Поскольку массовое назначение - это популярное и многоразовое поведение, имеет смысл извлечь его в отдельный модуль и сделать его миксом:
module MassAssignment
def initialize(attributes = {})
mass_assign(attributes)
end
def mass_assign(attributes)
attributes.each do |attribute, value|
respond_to?(:"#{attribute}=") && send(:"#{attribute}=", value)
end
end
end
class Foo
include MassAssignment
end
. Вы можете использовать ввести объект параметра Метод рефакторинга для упрощения вызова конструктора.