web-dev-qa-db-ja.com

なぜプライベート変数が必要なのですか?

クラスにプライベート変数が必要なのはなぜですか?

私が読んだプログラミングに関するすべての本は、これがプライベート変数であると言っています。これはあなたがそれを定義する方法ですが、そこで止まります。

これらの説明の文言は、私たちの職業に対する信頼の危機が本当にあるように私には常に思えました。他のプログラマーが私たちのコードを台無しにしようとしているように、説明は常に聞こえました。しかし、プライベート変数を持たないプログラミング言語はたくさんあります。

  1. プライベート変数は何を防ぐのに役立ちますか?

  2. 特定のプロパティを非公開にするかどうかをどのように決定しますか?デフォルトですべてのフィールドがプライベートである必要がある場合、クラスにパブリックデータメンバーがあるのはなぜですか?

  3. どのような状況で変数を公開する必要がありますか?

216
mwallace

それはそれほど信頼の問題ではなく、複雑さを管理することの1つです。

パブリックメンバーは、クラスの外部からアクセスできます。これは、実用的な考慮事項として、「どこにでも」という意味です。パブリックフィールドで問題が発生した場合、犯人はどこにでもいる可能性があるため、バグを追跡するには、かなり多くのコードを調べる必要がある場合があります。

対照的に、プライベートメンバーは同じクラス内からのみアクセスできるため、何か問題が発生した場合でも、通常、参照するソースファイルは1つだけです。プロジェクトに100万行のコードがあり、クラスが小さく保たれている場合、バグ追跡の労力を1000分の1に減らすことができます。

別の利点は、「カップリング」の概念に関連しています。別のクラスmが使用するクラスAのパブリックメンバーBは、依存関係を導入します。mAを変更すると、 m内のBの使用法を確認してください。さらに悪いことに、Aクラスがmがどこで使用されているかを示すものがないため、コードベース全体を検索する必要があります。それがあなたが書いているライブラリである場合、コードを確認する必要さえありますoutside変更のためにプロジェクトが壊れないようにします。実際には、ライブラリは、どれほど苦痛があったとしても、可能な限り元のメソッドシグネチャに固執する傾向があり、メジャーバージョンアップデートで重大な変更のブロックを導入します。対照的に、プライベートメンバーを使用すると、依存関係をすぐに除外できます。依存関係は外部からアクセスできないため、すべての依存関係はクラス内に含まれます。

この文脈では、「他のプログラマー」はあなたの未来と過去の自分自身を含みます。おそらくあなたは知っています変数YでこのことXを行うべきではありませんが、顧客が緊急に機能を実装する必要があるときに、3か月も忘れてしまいます。そして、なぜXを実行するとYが曖昧な方法で壊れてしまうのでしょうか。

したがって、いつプライベートにする必要があるかについては、デフォルトですべてをプライベートにし、公開する必要がある部分だけを公開します。プライベートにすることができればそれだけ多くなります。

312
tdammers

プライベート変数は、ユーザーがコードの特定の部分に依存するのを防ぐのに役立ちます。たとえば、データ構造を実装したいとします。データ構造のユーザーに、実装した方法howを気にせず、明確に定義されたインターフェースを介して実装を使用することを望んでいます。その理由は、実装に依存している人がいない場合は、いつでも変更できるためです。たとえば、バックエンドの実装を変更してパフォーマンスを向上させることができます。あなたの実装に依存していた他の開発者は、インターフェースのユーザーは問題なく、壊れます。クラスのユーザーに影響を与えずに実装を変更できる柔軟性があることは、プライベート変数(より広範には encapsulation )を使用することで得られる大きな利点です。

また、それは実際には「信頼の危機」ではありません。データを公開した場合、誰もそのデータに依存していないことを確認することはできません。特に締め切りの暑さの中で、パブリックインターフェイスを経由する代わりに、実装固有の変数に依存することは非常に便利です。さらに、開発者は、変更される可能性のあるものに依存していることに常に気付くわけではありません。

こうすれば、他の質問に答えられるといいのですが。すべての実装の詳細はプライベートである必要があり、パブリック部分はクラスを使用するための小さくて簡潔で明確に定義されたインターフェースである必要があります。

111
Oleksi

ここでのキーワードは カプセル化 です。 OOPでは、オブジェクト/クラスを適切にカプセル化するためにプライベート変数を使用する必要があります。

他のプログラマーがあなたを誘惑するわけではありませんが、彼らはあなたのコードと相互作用します。変数をプライベートにしないと、害を及ぼすことなく、独自のコードで変数を参照できます。ただし、クラスに戻って何かを変更する必要がある場合、誰がどの変数をどこで使用しているのかわからなくなります。カプセル化の目的は、クラスの外部インターフェイスを明示的にして、これらの(通常)メソッドだけが他のユーザーによって使用された可能性があることを理解することです。

  1. したがって、プライベート変数は、対応する変数が定義クラスのみに残ることを保証します。変更する必要がある場合、変更はクラス自体に対してローカルです。

  2. C++やJavaなどの従来の言語では、通常、すべてをプライベートにし、対応するゲッターとセッターだけがアクセスできるようにします。本当に多くを決める必要はありません。

  3. 時々、f.ex。 C++構造体では、クラスは、いくつかのものをグループ化する方法としてのみ必要です。たとえば、xおよびy属性のみを持つVectorクラスを考えてみます。このような場合、これらの属性をパブリックに宣言することにより、これらの属性への直接アクセスを許可できます。特に、外部のコードがxまたはyに直接新しい値を書き込んでクラスのオブジェクトを変更するかどうかを気にする必要はありません。

追加ポイントとして、この問題は他の言語では少し異なると見なされていることに注意してください。たとえば、関数型プログラミングに強く根ざしている言語は、データの不変性を強調しています。つまり、データ値はまったく変更できません。そのような場合、他のプログラマー(またはそのコード)がデータに対して行うことを気にする必要はありません。すべてのデータは不変であるため、コードに影響を与える可能性のあることは何もできません。

したがって、これらの言語では niform Access Principle と呼ばれるものを取得します。ここでは、ゲッターとセッターのようなメソッドを慎重に区別せず、変数/関数への直接アクセスを提供します。したがって、オブジェクト指向のシナリオでは、プライベート宣言のほうがはるかに人気があると言えます。

これは、他のフィールドを含めるために知識を広げることで、既存の概念をまったく新しい方法で表示する方法も示しています。

26
Frank

プライベート変数を使用すると、後でオブジェクトまたは関数のスコープ外で参照しても、他の変数に誤って影響を与えることがなくなります。大規模なプログラミングプロジェクトの場合、これは、多くの興味深い問題(通常はコンパイラーで検出されない)を回避するのに役立ちます。

たとえば、JavaScriptのようなプロトタイプ言語を使用すると、ユーザーは変数が適切であると見なすことができます。

_function SomeObject() {
    this.a = 2; //Public (well, actually protected) variable

    this.showA = function() {
        alert(this.a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 3. Uh oh!
_

その変数がプライベートの場合、外部から変更することはできません。

_function SomeObject() {
    var a = 2; //Private variable

    this.showA = function() {
        alert(a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 2
_

とはいえ、すべての変数がプライベートである必要はなく、プライベート変数を過度に使用すると、実際にコードが扱いにくくなる可能性があります。オブジェクトに重大な影響を与えない自明なフィールドには、get()およびset()メソッドは必要ありません。

プライベート変数を使用すると、多くのパブリック変数が何をするかについての重いドキュメントに頼ることなく、将来のコードの拡張が容易になります。上書きできる変数の数が減れば、誤って機能を壊してしまう恐れはほとんどありません。

オブジェクト/関数が他のオブジェクト/関数にアクセスするときは、パブリック変数を使用する必要があります。これは、原則として、プライベート変数ではこれができないためです。いくつかのパブリック変数からかなりの数のパブリック変数があり、それらが正しく使用されている場合、それらを使用することは悪いことではありません。

7
Jeffrey Sweeney

クラスの将来のメンテナーとユーザーにとっての利点を引用するいくつかの良い答えがあります。ただし、元のデザインには利点もあります。これは、自分の目標を簡単にすることと、クラスのユーザーが簡単にすることの明確な境界を示します。パブリックとプライベートのどちらかを選択すると、どちらかのモードに切り替えるように脳に通知され、より優れたAPIになります。

プライバシーのない言語がそのようなデザインを不可能にするということではありません。 foo.getBar()のようなものを書くように促されるのではなく、foo.barを書く方が簡単なので、人々はゲッターは必要ないと考えがちですが、後でその決定は再訪されませんobj.container[baz].foo.barのような長い怪物になってしまいます。厳密な言語で作業したことがないプログラマは、何が悪いのかさえわからないかもしれません。

そのため、プライバシーのない言語では、すべての「プライベート」メンバーにアンダースコアを付加するなど、それをシミュレートする命名基準を採用することがよくあります。言語が強制していない場合でも、これは有用なシグナルです。

7
Karl Bielefeldt

すべての変数は、絶対にパブリックにする必要がない限り、プライベートにする必要があります(ほとんどの場合、プロパティ/ゲッターとセッターを使用する必要があります)。

変数は主にオブジェクトの状態を提供し、プライベート変数は他のユーザーがオブジェクトの状態に入って状態を変更するのを防ぎます。

3
bbb

SOに関する関連する質問に対する私の私の投稿 を参照してください。

つまり、変数スコープを使用すると、コードのコンシューマーに、何をすべきで何をいじるべきでないかを示すことができます。プライベート変数は、プロパティセッターまたは処理メソッドを使用して「検査」されたデータを保持し、オブジェクト全体が一貫した状態であることを確認できます。プライベート変数の値を直接変更すると、オブジェクトが不整合になる可能性があります。それをプライベートにすることで、誰かがそれを変更するために、本当に一生懸命に働き、そして非常に高いランタイム権限を持たなければなりません。

したがって、自己文書化コードでは、適切なメンバーのスコープが重要です。

2
KeithS

これは、攻撃に対する信頼や恐れとは関係がなく、単にカプセル化に関するものであり、クラスのユーザーに不要な情報を強制するものではありません。

プライベート定数を検討してください-シークレット値(それらは他の場所に格納する必要があります)を含めることはできません。変更することはできません。クラスに渡す必要はありません(そうでない場合、パブリックにする必要があります)。それらの唯一の可能な使用は、他のクラスの定数としてです。しかし、それを行うと、それらのクラスは、クラスに依存しない作業を行うために、クラスに依存するようになります。定数を変更すると、他のクラスが壊れる可能性があります。これはどちらの面からも悪いことです。クラスの作成者として、あなたは自由を可能な限り変更することを望み、自分の制御の外にあるものについて心配したくありません。クラスのコンシューマーは、クラスの変更やコードの破損を心配することなく、クラスの公開された詳細に依存できることを望んでいます。

クラスのコンシューマーは、クラスとやり取りするために必要なすべてのことを知りたいと思っています。クラスの方法を変えないクラスについては知りたくありません。それは役に立たない雑学です。リフレクションで言語を使用したことがある場合、あるクラスがどのように何かをしたり、予期せず例外をスローしたりするのではなく、単にプライベートフィールドとメソッドの名前を学習するためにどのくらいの頻度で使用しましたか?私は賭けません。あなたはその知識を使う必要がないからです。

2
jmoreno
  • プライベート変数は何を防ぐのに役立ちますか?

それは、どのプロパティとメソッドがインターフェイスであり、どれが実際にコア機能であるかを明確にすることです。パブリックメソッド/プロパティは、多くの場合、オブジェクトを使用する他の開発者のコ​​ードに関するものです。同じプロジェクトで100以上のチームに取り組んだことはありませんが、私が書いたものを使用して最大20人の他の開発者がいる3〜5人のチームの経験では、他の懸念事項はばかげているように見えます。

注:私は主にJavaScript開発者です。私は通常、他の開発者が自分のコードを表示することを心配していません。私は、いつでも自分のものを再定義できるという十分な認識のもとで活動しています。私は彼らが最初に何をしているのかを知るのに十分な能力があると思います。そうでない場合、ソース管理を使用しないチームで作業することはほとんどありません。

  • 特定のプロパティセットをプライベートにするかどうかをどのように決定しますか?デフォルトですべてのフィールドがプライベートである必要がある場合、クラスにパブリックデータメンバーがあるのはなぜですか?

設定するプロパティの検証やその他の特別な処理を実際に実行していないときに、プライベートプロパティにゲッターとセッターを配置するのはばかげていると思っていました。私はまだそれが常に必要であるとは思いませんが、大規模で高度な複雑さを扱っている場合、最も重要なのは、一貫した方法でオブジェクトの動作に依存している他の多くの開発者が、一貫して均一な方法。人々がgetThatsetThatと呼ばれるメソッドを見るとき、彼らは意図が何であるかを正確に知っており、彼らは彼らがトマトトスではなく常にトマトを手に入れることを期待してあなたのオブジェクトと対話するようにオブジェクトを書くことができます。そうでない場合、彼らはあなたのオブジェクトが彼らにおそらくすべきでない何かを与えていることを知っています、それはそれが他のオブジェクトがそれがおそらくすべきでないデータで何かをすることを許可していることを意味します。必要に応じて制御できます。

もう1つの大きな利点は、さまざまな状態のコンテキストに基づいて、ゲッターやセッターとは異なる方法でオブジェクトに値を渡したり解釈したりするのが簡単になることです。彼らはメソッドであなたのものにアクセスしているので、それに依存する他のコードを壊すことなく、あなたのオブジェクトが後でどのように動作するかを変更することははるかに簡単です。重要な原則は、オブジェクトだけが実際に、それを担当させたデータを変更するということです。これは、コードの疎結合を維持するために特に重要です。これは、移植性と変更の容易さの点で大きな勝利です。

  • どのような状況で変数を公開する必要がありますか?

ファーストクラスの関数(引数として関数を渡すことができます)を使用する場合、小規模なプロジェクトではそうする必要がないこと以外に、多くの大きな理由を考えることはできません。それがなければ、他のオブジェクトによって頻繁に定期的に処理されているメンバーがいる可能性がありますが、そのデータ自体に実際には触れていないオブジェクトに対してgetおよびsetを常に呼び出すのは少し手間がかかるようですが、それ自体は、なぜオブジェクトがそのデータを担当するのか不思議に思うでしょう。一般的に言えば、パブリックデータプロパティを決定する必要がある前に、アーキテクチャの欠陥を再検討したいと思う傾向があります。 IMO、通常悪い考えであることができるようにする言語に問題はありません。一部の言語は同意しません。

2
Erik Reppen

実際の例を使用して、カプセル化の価値について別の視点から説明します。かなり前(80年代前半)に、Radion Shack Color Computer用のゲームがDungeons of Daggorathと呼ばれていました。数年前、Richard Hunerlachはそれを紙のアセンブラリストからC/C++( http://mspencer.net/daggorath/dodpcp.html )に移植しました。しばらくして、コードを取得し、カプセル化を改善するためにコードをリファクタリングし始めました( http://dbp-consulting.com/stuff/ )。

コードは、スケジューリング、ビデオ、ユーザー入力、ダンジョン作成、モンスターなどを処理するために、すでにさまざまなオブジェクトに組み込まれていますが、アセンブラーから移植されたことがはっきりとわかります。私の最大の仕事は、カプセル化を増やすことでした。つまり、コードのさまざまな部分を取得して、お互いのビジネスをうまくやりくりすることです。たとえば、ビデオ変数を直接変更して何らかの影響を与える場所はたくさんありました。エラーチェックでそれを実行する人もいれば、そうでない人もいます。コードの重複が多かった。代わりに、ビデオセクションには、目的のタスクを実行するためのインターフェイスが必要でした。そのためのコードは、すべて1つの場所、1つのアイデア、1つの場所でデバッグできます。そのようなことは蔓延していた。すべてのオブジェクトが他のすべてのオブジェクトに直接アクセスし、コードがどこにでも複製されました。

コードがばらばらになり始めたので、診断さえされていなかったバグはなくなりました。コードはより高品質になりました。各タスクはコード内の1つの場所の責任となり、それを正しく行う場所は1つしかありませんでした。 (コードをもう一度パスするたびに、さらに多くが見つかります。)

あなたのコードについて目に見えるすべてがあなたのインターフェースです。それが意味するかどうか。可視性を可能な限り制限すると、後でコードを間違った場所に配置したくなくなります。これにより、コードのどの部分がどのデータを担当するかが明確になります。それは、より良く、よりクリーンで、よりシンプルで、よりエレガントなデザインになります。

オブジェクトに独自のデータの責任を持たせ、何かを行う必要のある他のすべての人にインターフェースを提供すると、コードのLOTがさらにシンプルになります。落ちるだけです。あなたはたった1つのことを行う小さな単純なルーチンを持っています。複雑さの軽減==バグの減少。

2
phorgan1

OOPコンセプトには継承にその機能の1つ(JavaまたはC++)があります。そのため、継承する場合(つまり、継承されたクラスの変数にアクセスすることを意味します)、これらの変数に影響を与えるため、変数を変更できるかどうかを決定する必要があります。

この目的のためだけに、OOPでアクセス修飾子を使用しています。修飾子の1つはプライベートです。つまり、そのクラスだけがアクセスできます。他のクラスはこれらの変数に影響を与えることができません。

ご存知のように、保護されているとは、継承するクラスからアクセスできることを意味します。

OOPに修飾子の概念がある理由は、これらの理由によるものです(どのクラスもOOPの概念を使用して他のクラス変数にアクセスできます)。修飾子の概念がない場合、それは困難でした。クラスの継承、または他のOOPの概念の使用。

1
rajNaveen

他の人が述べたように、プライベート変数は、オブジェクトを不整合なステータスに導く誤用を回避し、バグや予期しない例外を追跡するのが難しいのに適しています。

しかし、その一方で、他の人たちによってほとんど無視されてきたのは、保護されたフィールドについてです。

拡張サブクラスは保護されたフィールドへの完全なアクセス権を持ち、そのようなフィールドがパブリックであるかのようにオブジェクトを脆弱にしますが、その脆弱性は拡張クラス自体に制限されます(そのようなフィールドをさらに公開しない限り)。

したがって、パブリックフィールドを適切と見なすことは困難であり、現在までそれらを使用する唯一の理由は、構成パラメーターとして使用されるクラス(多くのフィールドとロジックのない非常に単純なクラスであるため、クラスはパラメーターとしてだけに渡されます)いくつかの方法)。

しかし一方で、プライベートフィールドは、他のユーザーにとってコードの柔軟性を低下させます。

柔軟性vsトラブル、長所と短所:

保護されたフィールドを持つVanillaクラスのコードによってインスタンス化されたオブジェクトは安全であり、ユーザーの責任です。

一方、保護されたフィールドを使用してクラスを拡張し、コードのユーザーによってインスタンス化されたオブジェクトは、ユーザーではなく、ユーザーの責任です。

したがって、十分に文書化された保護フィールド/メソッドがない場合、またはユーザーがそのようなフィールドとメソッドの使用方法を本当に理解していない場合は、自分自身とあなたに不要なトラブルを引き起こす可能性が高くなります。

一方、ほとんどのものを非公開にすると、ユーザーの柔軟性が低下し、維持された代替手段を探す必要がなくなることもあります。

したがって、プライベート、保護、パブリックのバランスが重要です。

さて、プライベートかプロテクトかを決めるのが本当の問題です。

保護を使用する場合?

フィールドが非常に柔軟であることを理解するたびに、フィールドは保護されているものとしてコーディングする必要があります。その柔軟性は、nullになる(nullは常にチェックされ、例外をスローしない有効な状態として認識される)から、クラスexで使用される前に制約を持つまでです。 > = 0、<100など、オーバーフロー/アンダーフロー値に対して自動的に修正され、最大で警告メッセージがスローされます。

したがって、そのような保護されたフィールドの場合、ゲッターを作成して(フィールド変数を直接使用する代わりに)ゲッターのみを使用することができますが、他のユーザーはそれを使用しない場合があります。 :拡張クラスで負の値がうまく機能するようにする場合。

1
Aquarius Power

imo @tdammersは正しくなく、実際には誤解を招く可能性があります。

プライベート変数は、実装の詳細を隠すために存在します。クラスAarrayを使用してスコアを保存している可能性があります。明日は、代わりにtreeまたは_priority queue_を使用することをお勧めします。クラスのすべてのユーザーは、スコアと名前を入力する方法addScore()と、トップ10 getTop(n)が誰であるかを把握する方法です。

基盤となるデータ構造にアクセスする必要はありません。いいね?まあ、いくつかの注意点があります。

とにかく、多くの状態を保存するべきではありません。ほとんどの状態は必要ありません。状態を保存すると、特定のクラスに「分離」されている場合でも、コードが複雑になります。考えてみてください。そのプライベート変数は、別のオブジェクトがそのクラスのメソッドを呼び出したためにおそらく変更されました。そのメソッドがいつどこで呼び出されたか、またそのパブリックメソッドは理論的にはどこでも呼び出すことができるかを把握する必要があります。

あなたができる最善のことは、プログラムが含む状態の量を制限し、可能であれば純粋な関数を使用することです。

0
AturSams