41

Rubyでインスタンス変数を「プライベート」(C++またはJava定義)にする方法はありますか? つまり、次のコードでエラーが発生するようにしたいと考えています。

class Base
  def initialize()
    @x = 10
  end
end

class Derived < Base
  def x
    @x = 20
  end
end

d = Derived.new
4

6 に答える 6

38

Rubyのほとんどのものと同様に、インスタンス変数は真に「プライベート」ではなく、。を使用して誰でもアクセスできますd.instance_variable_get :@x

ただし、Java / C ++とは異なり、Rubyのインスタンス変数は常にプライベートです。それらは、その冗長なゲッターでのみアクセスできるため、メソッドのようにパブリックAPIの一部になることはありません。したがって、APIに正常性がある場合は、代わりにメソッドを使用するため、誰かがインスタンス変数を悪用することを心配する必要はありません。(もちろん、誰かがワイルドになってプライベートメソッドやインスタンス変数にアクセスしたい場合、それらを止める方法はありません。)

唯一の懸念は、誰かがクラスを拡張するときに誤ってインスタンス変数を上書きした場合です。これは、おそらく@base_xあなたの例でそれを呼び出すなど、ありそうもない名前を使用することで回避できます。

于 2010-01-25T13:01:57.880 に答える
29

インスタンス変数を直接使用しないでください。アクセサーのみを使用してください。次の方法で、リーダーをパブリックとして、ライターをプライベートとして定義できます。

class Foo
  attr_reader :bar

  private

  attr_writer :bar
end

ただし、それらが意味することを意味するものではないことprivateに注意してください。パブリック メソッドは、名前付き、自己、または暗黙的 ( 、、または)protectedの任意の受信者に対して呼び出すことができます。保護されたメソッドは、self のレシーバーを使用して、または暗黙的に ( , ) のみ呼び出すことができます。プライベート メソッドは、暗黙的なレシーバー ( ) でのみ呼び出すことができます。x.bazself.bazbazself.bazbazbaz

簡単に言えば、Ruby 以外の観点から問題に取り組んでいるということです。インスタンス変数の代わりにアクセサーを常に使用します。public/ protected/を使用privateして意図を文書化し、API の利用者は責任ある成人であると想定してください。

于 2012-01-24T17:30:14.190 に答える
14

あなたが求めていることを正確に行うことは可能です(しかしお勧めできません)。

望ましい動作には 2 つの異なる要素があります。1 つ目は読み取り専用の valueに格納することxで、2 つ目はgetterがサブクラスで変更されないように保護することです。


読み取り専用値

Ruby では、初期化時に読み取り専用の値を格納することができます。これを行うために、Ruby ブロックのクロージャ動作を使用します。

class Foo
  def initialize (x)
    define_singleton_method(:x) { x }
  end
end

の初期値はx、getter の定義に使用したブロック内にロックされ、 を#x呼び出す以外にアクセスfoo.xできず、変更することもできません。

foo = Foo.new(2)
foo.x  # => 2
foo.instance_variable_get(:@x)  # => nil

インスタンス変数として保存されていないことに注意して@xくださいdefine_singleton_method


ゲッターの保護

Ruby では、実行時に任意のクラスのほぼすべてのメソッドを上書きできます。method_addedフックを使用してこれを防ぐ方法があります。

class Foo
  def self.method_added (name)
    raise(NameError, "cannot change x getter") if name == :x
  end
end

class Bar < Foo
  def x
    20
  end
end

# => NameError: cannot change x getter

これは、ゲッターを保護するための非常に手間のかかる方法です。

保護された各ゲッターをmethod_addedフックに個別に追加する必要があります。その場合でも、コーダーがメソッド自体を上書きするのを防ぐために、 とそのサブクラスに別のレベルのmethod_added保護を追加する必要があります。Foomethod_added

Ruby を使用している場合、実行時のコード置換は避けられないことです。

于 2014-02-18T03:24:38.320 に答える
5

さまざまなレベルの可視性を持つメソッドとは異なり、Ruby インスタンス変数は常にプライベート (オブジェクトの外部から) です。ただし、オブジェクト内のインスタンス変数は、親クラス、子クラス、または含まれるモジュールのいずれかから常にアクセスできます。

Ruby のアクセス方法を変更する方法はおそらくないので@x、それを制御することはできないと思います。書き込み@xはそのインスタンス変数を直接選択するだけであり、Ruby は変数の可視性制御を提供していないため、それでうまくいくと思います。

@marcggが言うように、派生クラスがインスタンス変数に触れたくない場合は、それをまったく使用しないか、派生クラスから見えないようにする巧妙な方法を見つけてください。

于 2010-01-26T02:47:10.377 に答える
2

インスタンス変数はクラスではなくオブジェクトによって定義されるため、必要なことを行うことはできません。

継承ではなく構成を使用する場合、インスタンス変数の上書きについて心配する必要はありません。

于 2012-01-24T22:01:44.763 に答える
-1

これが古いことは知っていますが、@x へのアクセスをあまり防止したくない場合に遭遇しました。シリアライゼーションにリフレクションを使用するすべてのメソッドから @x を除外したかったのです。具体的にはYAML::dump、デバッグ目的でよく使用します。私の場合、 @x は classClassでありYAML::dump、ダンプを拒否します。

この場合、私はいくつかのオプションを検討しました

  1. 「to_yaml_properties」を再定義することにより、yaml だけでこれに対処します。

    def to_yaml_properties
      super-["@x"]
    end
    

    しかし、これは yaml だけで機能し、他のダンパー ( to_xml?) が満足しない場合

  2. 「instance_variables」の再定義によるすべてのリフレクション ユーザーのアドレス指定

    def instance_variables
      super-["@x"]
    end
    
  3. また、検索の1つでこれを見つけましが、上記のほうが私のニーズに合わせて簡単に見えるため、テストしていません

したがって、これらはOPが必要としていると正確に述べたものではないかもしれませんが、アクセスではなくリストから除外する変数を探しているときに他の人がこの投稿を見つけた場合、これらのオプションは価値があるかもしれません.

于 2012-05-08T23:16:50.353 に答える