13

このパターンを抽象化する最良の方法は次のとおりです。

class MyClass
  attr_accessor :foo, :bar

  def initialize(foo, bar)
    @foo, @bar = foo, bar
  end
end

優れたソリューションは、スーパークラスを考慮入れ、より多くのことを行うためのイニシャライザを引き続き使用できるようにする必要があります。ソリューションのパフォーマンスを犠牲にしないための余分なポイント。

4

5 に答える 5

7

その問題の解決策は既に (部分的に)存在しますが、クラスでより宣言的なアプローチが必要な場合は、次のように動作するはずです。

class Class
  def initialize_with(*attrs, &block)
    attrs.each do |attr|
      attr_accessor attr
    end
    (class << self; self; end).send :define_method, :new do |*args|
      obj = allocate
      init_args, surplus_args = args[0...attrs.size], args[attrs.size..-1]
      attrs.zip(init_args) do |attr, arg|
        obj.instance_variable_set "@#{attr}", arg
      end
      obj.send :initialize, *surplus_args
      obj
    end
  end
end

次のことができるようになりました。

class MyClass < ParentClass
  initialize_with :foo, :bar
  def initialize(baz)
    @initialized = true
    super(baz) # pass any arguments to initializer of superclass
  end
end
my_obj = MyClass.new "foo", "bar", "baz"
my_obj.foo #=> "foo"
my_obj.bar #=> "bar"
my_obj.instance_variable_get(:@initialized) #=> true

このソリューションの特徴:

  • でコンストラクター属性を指定しますinitialize_with
  • オプションinitializeで、カスタム初期化を行うために使用します
  • super呼び込み可能initialize
  • 引数 toinitializeは、で指定された属性によって消費されなかった引数ですinitialize_with
  • モジュールに簡単に抽出
  • で指定されたコンストラクター属性initialize_withは継承されますが、子クラスで新しいセットを定義すると、親属性が削除されます
  • 動的ソリューションにはおそらくパフォーマンス ヒットがあります

パフォーマンスのオーバーヘッドを最小限に抑えたソリューションを作成したい場合、ほとんどの機能を文字列にリファクタリングすることはそれほど難しくありません。これevalは、初期化子の定義時に編集できます。私は違いが何であるかをベンチマークしていません。

new注: ハッキングよりもハッキングの方が効果的であることがわかりましたinitialize。メタプログラミングで定義すると、ブロックを代用イニシャライザとしてinitialize渡すシナリオが発生する可能性があり、ブロック内で使用することはできません。initialize_withsuper

于 2009-07-03T07:56:18.220 に答える
1

これが私の頭に浮かぶ最初の解決策です。私のモジュールには大きな欠点が1つあります。モジュールを含める前に、クラス初期化メソッドを定義する必要があります。そうしないと、機能しません。

その問題にはおそらくもっと良い解決策がありますが、これは私が数分以内に書いたものです。

また、パフォーマンスもあまり考慮していませんでした。特にパフォーマンスについて話すと、おそらく私よりもはるかに優れた解決策を見つけることができます。;)

#!/usr/bin/env ruby -wKU

require 'rubygems'
require 'activesupport'


module Initializable

  def self.included(base)
    base.class_eval do
      extend  ClassMethods
      include InstanceMethods
      alias_method_chain :initialize, :attributes
      class_inheritable_array :attr_initializable
    end
  end

  module ClassMethods

    def attr_initialized(*attrs)
      attrs.flatten.each do |attr|
        attr_accessor attr
      end
      self.attr_initializable = attrs.flatten
    end

  end

  module InstanceMethods

    def initialize_with_attributes(*args)
      values = args.dup
      self.attr_initializable.each do |attr|
        self.send(:"#{attr}=", values.shift)
      end
      initialize_without_attributes(values)
    end

  end

end


class MyClass1
  attr_accessor :foo, :bar

  def initialize(foo, bar)
    @foo, @bar = foo, bar
  end
end

class MyClass2

  def initialize(*args)
  end

  include Initializable

  attr_initialized :foo, :bar
end


if $0 == __FILE__
  require 'test/unit'

  class InitializableTest < Test::Unit::TestCase

    def test_equality
      assert_equal MyClass1.new("foo1", "bar1").foo, MyClass2.new("foo1", "bar1").foo
      assert_equal MyClass1.new("foo1", "bar1").bar, MyClass2.new("foo1", "bar1").bar
    end

  end
end
于 2009-07-03T07:26:49.097 に答える
1
class MyClass < Struct.new(:foo, :bar)
end
于 2009-07-04T02:34:32.883 に答える