Rubyでインスタンス変数を「プライベート」(C++またはJava定義)にする方法はありますか? つまり、次のコードでエラーが発生するようにしたいと考えています。
class Base
def initialize()
@x = 10
end
end
class Derived < Base
def x
@x = 20
end
end
d = Derived.new
Rubyでインスタンス変数を「プライベート」(C++またはJava定義)にする方法はありますか? つまり、次のコードでエラーが発生するようにしたいと考えています。
class Base
def initialize()
@x = 10
end
end
class Derived < Base
def x
@x = 20
end
end
d = Derived.new
Rubyのほとんどのものと同様に、インスタンス変数は真に「プライベート」ではなく、。を使用して誰でもアクセスできますd.instance_variable_get :@x
。
ただし、Java / C ++とは異なり、Rubyのインスタンス変数は常にプライベートです。それらは、その冗長なゲッターでのみアクセスできるため、メソッドのようにパブリックAPIの一部になることはありません。したがって、APIに正常性がある場合は、代わりにメソッドを使用するため、誰かがインスタンス変数を悪用することを心配する必要はありません。(もちろん、誰かがワイルドになってプライベートメソッドやインスタンス変数にアクセスしたい場合、それらを止める方法はありません。)
唯一の懸念は、誰かがクラスを拡張するときに誤ってインスタンス変数を上書きした場合です。これは、おそらく@base_x
あなたの例でそれを呼び出すなど、ありそうもない名前を使用することで回避できます。
インスタンス変数を直接使用しないでください。アクセサーのみを使用してください。次の方法で、リーダーをパブリックとして、ライターをプライベートとして定義できます。
class Foo
attr_reader :bar
private
attr_writer :bar
end
ただし、それらが意味することを意味するものではないことprivate
に注意してください。パブリック メソッドは、名前付き、自己、または暗黙的 ( 、、または)protected
の任意の受信者に対して呼び出すことができます。保護されたメソッドは、self のレシーバーを使用して、または暗黙的に ( , ) のみ呼び出すことができます。プライベート メソッドは、暗黙的なレシーバー ( ) でのみ呼び出すことができます。x.baz
self.baz
baz
self.baz
baz
baz
簡単に言えば、Ruby 以外の観点から問題に取り組んでいるということです。インスタンス変数の代わりにアクセサーを常に使用します。public
/ protected
/を使用private
して意図を文書化し、API の利用者は責任ある成人であると想定してください。
あなたが求めていることを正確に行うことは可能です(しかしお勧めできません)。
望ましい動作には 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
保護を追加する必要があります。Foo
method_added
Ruby を使用している場合、実行時のコード置換は避けられないことです。
さまざまなレベルの可視性を持つメソッドとは異なり、Ruby インスタンス変数は常にプライベート (オブジェクトの外部から) です。ただし、オブジェクト内のインスタンス変数は、親クラス、子クラス、または含まれるモジュールのいずれかから常にアクセスできます。
Ruby のアクセス方法を変更する方法はおそらくないので@x
、それを制御することはできないと思います。書き込み@x
はそのインスタンス変数を直接選択するだけであり、Ruby は変数の可視性制御を提供していないため、それでうまくいくと思います。
@marcggが言うように、派生クラスがインスタンス変数に触れたくない場合は、それをまったく使用しないか、派生クラスから見えないようにする巧妙な方法を見つけてください。
インスタンス変数はクラスではなくオブジェクトによって定義されるため、必要なことを行うことはできません。
継承ではなく構成を使用する場合、インスタンス変数の上書きについて心配する必要はありません。
これが古いことは知っていますが、@x へのアクセスをあまり防止したくない場合に遭遇しました。シリアライゼーションにリフレクションを使用するすべてのメソッドから @x を除外したかったのです。具体的にはYAML::dump
、デバッグ目的でよく使用します。私の場合、 @x は classClass
でありYAML::dump
、ダンプを拒否します。
この場合、私はいくつかのオプションを検討しました
「to_yaml_properties」を再定義することにより、yaml だけでこれに対処します。
def to_yaml_properties
super-["@x"]
end
しかし、これは yaml だけで機能し、他のダンパー ( to_xml
?) が満足しない場合
「instance_variables」の再定義によるすべてのリフレクション ユーザーのアドレス指定
def instance_variables
super-["@x"]
end
また、検索の1つでこれを見つけましたが、上記のほうが私のニーズに合わせて簡単に見えるため、テストしていません
したがって、これらはOPが必要としていると正確に述べたものではないかもしれませんが、アクセスではなくリストから除外する変数を探しているときに他の人がこの投稿を見つけた場合、これらのオプションは価値があるかもしれません.