変数にゲッターとセッターがある場合、それはパブリックである必要がありますか?


23

プライベートな変数を持つクラスがあり、クラスにはその変数のゲッターとセッターがあります。なぜその変数を公開しないのですか?

ゲッターとセッターを使用する必要があると思うのは、setまたはget以外の操作を行う必要がある場合だけです。例:

void my_class::set_variable(int x){
   /* Some operation like updating a log */
   this->variable = x;
}

10
シールドにはゲッターとセッターがあります。いつかあなたがしたいthis->variable = x + 5、またはUpdateStatisticsセッターで関数を呼び出すと、そのような場合classinstancea->variable = 5に問題が発生します。
コーダー

1
別の例である:set_inchset_centimeterget_inchget_centimeter、いくつかの不気味なアクションを持ちます。
-rwong

うん、ゲッターとセッターは、インスタンス変数を制御したい方法で制御するのに役立ちます。
developerdoug

7
@Coder:必要に応じて、ゲッター/セッターをいつでも追加できますか?または、これはC ++では簡単にできないことですか(私の経験はほとんどDelphiです)?
マルジャンヴェネマ

この質問:Programmers.stackexchange.com/questions/21802/…は興味深いかもしれません。
ウィンストンイーバート

回答:


21

不動産について聞いたことがありますか?

プロパティは、「組み込み」のアクセサー(ゲッターとセッター)を持つフィールドです。たとえば、Javaにはプロパティがありませんが、ゲッターとセッターをプライベートフィールドに書き込むことをお勧めします。C#にはプロパティがあります。

それでは、なぜゲッターとセッターが必要なのでしょうか?基本的に、フィールドを保護/シールドするために必要です。たとえば、メモリ参照のフィールドにアクセスするのではなく、フィールド(参照)を変更するメソッドにアクセスします。このメソッドは、あなたの例のように、ユーザーが知りたくないいくつかの操作(カプセル化動作)を実行できます。たとえば、12個のクラスがパブリックフィールドを使用し、使用方法を変更する必要があると想像してください。フィールドの使用方法を変更するには、これらの各クラスを確認する必要があります。だから「OOlysh」。

ただし、たとえば、deadというブール値フィールドがある場合。setDeadとisDeadを宣言する前によく考えてください。たとえば、setDeadの代わりにkill()のように、人間が読めるアクセサーを作成する必要があります。

ただし、JavaBeanの命名規則(ここではJavaについて話している)に従うことを前提とする多くのフレームワークがあります。したがって、そのような場合は、命名規則に従ってすべてのゲッターとセッターを宣言する必要があります。


2
今、「人間が読める」は、最終的に私が「grok」できる議論です。私は通常聞く必要が生じたときに同じように簡単に対処され、将来のプルーフのいくつかの並べ替え...ある他のすべての引数
マージャンVenema氏

13
「将来の校正」への軽daは一般的ですが、見当違いです。はい、そのコードの唯一のクライアントであれば、必要に応じてそのような変更に対処できます。外部クライアント向けにソリューションを公開しない場合、技術的な負債を負うだけで、誰も知る必要はありません。しかし、内部のプロジェクトは、あなたがそれを最も期待していなかったときに公開される習慣があり、それからあなたにとって悲惨なことです。
キリアンフォス

2
あなたは実際に素晴らしい点を上げます。killはset / getではなく、アクションです。それは実装を完全に隠します-それはあなたがオブジェクト指向をコーディングする方法です。一方、プロパティは、セッター/ゲッターと同じくらい愚かです-簡単な構文は、人々が必要でもないときにそれらを変数に追加することを奨励するからです(OO-Furbarが発生するのを待っています)。「プロパティ」タグをデッドフラグに追加するだけで、kill()を使用すると誰が考えるでしょうか。
ビルK

1
質問にはC ++質問がタグ付けされています。C ++はプロパティをサポートしていませんが、なぜこれを投稿するのでしょうか?
トーマスエディング

1
@ThomasEding:C ++は、オーバーライド可能な代入演算子を持つという点でやや独特です。C ++は、C#やVB.NETなどの言語と同じように「プロパティ」という用語を使用していませんが、概念はまだ存在しています。C ++では、演算子オーバーロードを使用しfoo.bar = biz.baz+5て関数on bizを呼び出して評価しbaz、5を追加して関数on fooを呼び出しbarて結果の値を設定することができます。このようなオーバーロードは「プロパティ」とは呼ばれませんが、同じ目的を果たすことができます。
-supercat

25

これは最も一般的な意見ではありませんが、大きな違いは見当たりません。

セッターとゲッターはかなり悪い考えです。私はそれについて考えましたが、正直なところ、実際にはセッター/ゲッタープロパティとパブリック変数の違いを思いつくことはできません。

理論では、セッターとゲッターまたはプロパティは、変数が設定/取得されたときに追加のアクションを実行する場所を追加し、理論的にはコードを変更から分離します。

実際には、アクションの追加にセッターとゲッターが使用されることはめったにありません。アクションを追加する場合は、クラスのすべてのセッターまたはゲッター(ロギングなど)に追加する必要があります。より良いソリューションになります。

設計の決定を分離する場合、intをlongに変更する場合は、セッターを変更し、少なくとも手動でアクセスするすべての行をチェックする必要があります。

とにかく、デフォルトでは変更可能なクラスを避ける必要があるため、セッターの追加は最後の手段です。これは、オブジェクトが目的の状態になるまで値を設定できるビルダパターンで緩和され、その後クラスが不変になり、セッターが例外をスローします。

ゲッターについては、まだゲッターとパブリック最終変数の間に大きな違いはありません。ここでの問題は、どちらの場合でもオブジェクト指向が悪いことです。オブジェクトに値を要求して操作するべきではありません。オブジェクトに操作を依頼する必要があります。

ところで、私は決してパブリック変数を支持していません。セッターとゲッター(さらにはプロパティ)はすでにパブリック変数に近すぎると言っています。

大きな問題は、オブジェクト指向プログラマーではない人がセッターとゲッターを使用してオブジェクトをプロパティボール(構造)に変えて誘惑することです。オブジェクト指向コードの動作とは正反対です。


ここでの他の答えとは別に、ゲッターセッターについての良い点は、実際のプライベート変数が値であると仮定する前に、その中にいくつかの検証/変換を追加できることです。
WinW

1
@Kiran trueですが、コンストラクターで設定した場合、検証を行うことができ、不変クラスがあります。セッターについては、書き込み可能なパブリック変数が良いと言っているのではなく、セッターさえ悪いと言っています。ゲッターにとっては、公開の最終変数を実行可能な代替手段と考えるかもしれません。
ビルK

C#などの一部の言語では、プロパティはパブリック変数とバイナリ互換ではないため、パブリック変数をAPIの一部にした後、検証を追加したい場合は、クライアントプログラムを変換するときにクライアントのプログラムを中断しますプロパティ。最初からパブリックプロパティにする方が良いでしょう。
ロバートハーヴェイ

@RobertHarveyそれはまだあなたがプロパティを公開していることを意味します。私の全体的なポイントは、プロパティの公開を避ける必要があり、必要に応じてゲッターに制限し、クラスを不変にすることを試みる必要があるということでした。クラスが不変である場合、なぜゲッターでコードが必要になるのでしょうか?したがって、ゲッターを持たない可能性もあります。可変プロパティを公開せずにクラスを書くことができない場合は、はい、セッターは書き込み可能なパブリック変数よりも常に優れています(そして、足で撃たれることは常に腸に刺されるよりも優れています)。
ビルK

1
可変状態は許容されますが、オブジェクトに何かをするのではなく、オブジェクトに何かをするようにオブジェクトに依頼する必要があります。したがって、状態を変更する場合、セッターはそれを行うのに最適な方法ではないので、ロジックを使用してビジネスメソッドを記述してくださいその代わりに。たとえば、「setZLocation(getZLocation()+ 5)ではなくmove(Up5)」。
ビルK

8

ゲッターとセッターのユーザーはカプセル化の原則に入ります。これにより、クラス内で物事がどのように機能するかを変更し、すべての機能を維持できます。

たとえば、3つの他のオブジェクトfoo.barがbarの値を取得するために呼び出し、barの名前を遠くに変更することにした場合、手に問題があります。オブジェクトが呼び出された場合、foo.barこれを持つすべてのクラスを変更する必要があります。セッター/ゲッターを使用する場合、変更するものは何もありません。別の可能性は、変数の型を変更することです。この場合、ゲッター/セッターに変換コードを追加するだけで問題ありません。


1
+1-メンバー変数は実装であり、ゲッターとセッターはインターフェースです。インターフェイスのみがパブリックである必要があります。また、ゲッターとセッターの名前は、それらの基礎となる単純なメンバー変数または他の実装があるかどうかに関係なく、抽象化の点で意味があります。ありますが、それだけで全体のサブシステムを表すクラスに、とにかくレベルアップデータ隠蔽境界をプッシュ-密結合のクラスから構築されたサブシステムが最も簡単な方法である場合-例外が。
Steve314

クラス内の作業を変更する必要があるときに、getterおよび/またはsetterを単純に導入することはできませんか?それはDelphiで簡単にできることですが、おそらく他の言語ではできませんか?
マルジャンヴェネマ

@ marjan確かに、後からいつでもゲッター/セッターを導入できます。問題は、ゲッター/セッターの代わりにパブリックフィールドを使用していたすべてのコードを変更する必要があることです。私が混乱しているのか、あなたがそうなのかわかりません。0.o
ピート

3
個人的には、これはちょっとした議論だと思います。変数の意味を変更しても、その変数にゲッターとセッターがある場合、それが何と呼ばれていてもかまいません。インターフェイスの目に見える部分ではありません。カプセル化された変数の意味の変更または明確化をクライアントに示すために、ゲッターとセッター自体の名前を変更する可能性がはるかに高くなります。後者の場合、パブリック変数を使用するよりも適切な位置にいます。これは、ゲッターとセッターを持つ変数がカプセル化されるよりも公開されているという議論とはまったく異なります。
CBベイリー

1
リファクタリング操作はどちらの方法でも事実上無料なので、私は実際にこれを購入しません。さらに、絶対に必要でない限り、セッターまたはゲッターを使用するのは非常に悪い形式です。最終的には、コンセプトを理解していない人々が、必要なときにシードを滴下するのではなく、メンバーにセッターとゲッターを自動的に追加するだけですコードベース全体の悪の。
ビルK

5

ゲッターとセッターを使用すると、特定の変数に保存するコンテンツを制御することもできます。コンテンツが特定のタイプまたは値である必要がある場合、セッターコードの一部は、新しい値がこれらの要件を満たしていることを確認することです。変数がパブリックの場合、これらの要件が満たされていることを確認できません。

このアプローチにより、コードの適応性と管理性も向上します。そのアーキテクチャを他のすべてのクラスまたはそのクラスを使用する関数から隠したままにする関数がある場合、クラスのアーキテクチャを簡単に変更できます。既に説明した変数名の変更は、ゲッターやセッターなどの関数を使用している場合に簡単に行える多くの変更の1つにすぎません。全体的な考え方は、特にクラス変数をできるだけプライベートにすることです。


どうもありがとう!私はあなたが言及したのと同じケースを考えていました。変数を[0,1]の内容にしたい場合は、そのセッターで入力値を確認する必要があります:)

あなたがプロジェクトに取り組んでいる唯一の開発者である場合、このようなことは役に立たないと考えるのは簡単ですが、チームで作業していることに気づくと、このようなテクニックの習慣があることが非常に役立ち、あなたを助けるでしょうたくさんのバグを回避してください。
ケネス

C#を使用している場合、プロパティを使用します。プロパティのセッター部分のロジックに基づいて、必要に応じてプロパティを使用してプライベートインスタンスを設定できます。
developerdoug

2

「getterとsetterを使用する必要があると思うのは、setまたはget以外の操作を行う必要がある場合だけです」と言います。

将来のある時点でsetおよびget以外の操作必要になる可能あり、その場合に数千行のソースコードを変更したくない場合は、getterおよびsetterを使用する必要があります。

誰かが変数のアドレスを取得してそれを渡すことを望まない場合は、ゲッターとセッターを使用する必要があります。


あなたの最後の段落は、両方の方法をカットする優れた点を示しています。重要な利点があり、外部コードが物事のアドレスを取得することのマイナス面がほとんどない場合、そのような制限は悪いことです。そのような動作を許可するメリットがほとんどなく、重大な欠点がある場合は、制限することは良いことです。
-supercat

1

最初に、パラダイムを明確にします。

  • データ構造->適切に知識のある機能によって走査および操作できるメモリのレイアウト。
  • オブジェクト->実装を隠し、通信可能なインターフェースを提供する自己完結型モジュール。

ゲッター/セッターはどこで便利ですか?

データ構造でゲッター/セッターは便利ですか?いや

データ構造は、一連の関数に共通して操作されるメモリレイアウト仕様です。

一般的に、古い新しい関数はすべて他の関数が理解できるようにデータ構造を操作し、操作できる場合、その関数はファミリに参加します。それ以外の場合、不正な機能とバグの原因となります。

誤解しないでください。スニッチ、ターンコート、および二重エージェントを含むデータ構造を巡って機能するいくつかの関数ファミリが存在する可能性があります。それぞれが独自のデータ構造を持っている場合は問題ありませんが、共有する場合は...いくつかの犯罪家族が政治に反対していると想像してみてください。

拡張機能ファミリが達成できる混乱を考えると、不正な機能がすべてを混乱させないようにデータ構造をエンコードする方法はありますか?はい、それらはオブジェクトと呼ばれます。

オブジェクトでゲッター/セッターは便利ですか?いや

データ構造をオブジェクトにラップすることの全体的な目的は、不正な機能が存在しないようにすることでした。関数がファミリーに参加したい場合は、最初に徹底的に吟味し、次にオブジェクトの一部にする必要がありました。

ゲッターとセッターのポイント/目的は、オブジェクトの外部の関数がオブジェクトのメモリレイアウトを直接変更できるようにすることです。それは悪党で許可するための開かれた扉のように聞こえます...

エッジケース

公共のゲッター/セッターが理にかなっている2つの状況があります。

  • オブジェクト内のデータ構造の一部はオブジェクトによって管理されますが、オブジェクトによって制御されません。
  • 一部の要素が実装オブジェクトを制御していないことが予想される、データ構造の高レベルの抽象化を記述するインターフェース。

コンテナとコンテナインターフェイスは、これら2つの状況の両方の完璧な例です。コンテナは、データ構造(リンクリスト、マップ、ツリー)を内部で管理しますが、特定の要素をすべておよびその他に制御します。インターフェイスはこれを抽象化し、実装を完全に無視し、期待のみを記述します。

残念ながら、多くの実装ではこれが間違っており、これらの種類のオブジェクトのインターフェースを定義して、実際のオブジェクトに直接アクセスできます。何かのようなもの:

interface Container<T>
{
    typedef ...T... TRef; //<somehow make TRef to be a reference or pointer to the memory location of T
    TRef item(int index); 
}

これは壊れています。Containerの実装は、内部の制御を使用者に明示的に渡す必要があります。これがうまくいく可変値言語を見たことはまだありません(不変値セマンティクスを持つ言語は、データ破損の観点からは定義上問題ありませんが、必ずしもデータスパイの観点からではありません)。

コピーセマンティクスのみを使用するか、プロキシを使用して、ゲッター/セッターを改善/修正できます。

interface Proxy<T>
{
     operator T(); //<returns a copy
     ... operator ->(); //<permits a function call to be forwarded to an element
     Proxy<T> operator=(T); //< permits the specific element to be replaced/assigned by another T.
}

interface Container<T>
{
     Proxy<T> item(int index);
     T item(int index); //<When T is a copy of the original value.
     void item(int index, T new_value); //<where new_value is used to replace the old value
}

おそらく、不正な機能はここで騒乱を起こす可能性があります(ほとんどの場合十分な労力で可能です)が、コピーセマンティクスやプロキシは多くのエラーの可能性を減らします。

  • オーバーフロー
  • アンダーフロー
  • サブ要素との相互作用は型チェック/型チェック可能です(型を失う言語ではこれは恩恵です)
  • 実際の要素は、メモリに常駐する場合としない場合があります。

プライベートゲッター/セッター

これは、型を直接操作するゲッターとセッターの最後の要塞です。実際、これらのゲッターとセッターではなく、アクセサーとマニピュレーターとも呼びます。

このコンテキストでは、データ構造の特定の部分を常に/ほぼ常に/一般的に操作するには、特定のブックキーピングを行う必要があります。ツリーのルートを更新するとき、ルックアサイドキャッシュを削除する必要がある、または外部データ要素にアクセスするときに、ロックを取得/解放する必要があるとします。これらの場合、DRYプリンシパルを適用し、それらのアクションをまとめてまとめることが理にかなっています。

プライベートコンテキスト内では、ファミリ内の他の関数がこれらの「ゲッターとセッター」を回避し、データ構造を操作することが可能です。したがって、なぜそれらをアクセサおよびマニピュレータと考えるのでしょうか。データに直接アクセスするか、別の家族に頼ってその部分を正しくすることができます。

保護されたゲッター/セッター

保護されたコンテキストでは、パブリックコンテキストとそれほど違いはありません。おそらく不正な外部機能は、データ構造へのアクセスを望んでいます。いいえ、存在する場合は、パブリックゲッター/セッターのように動作します。


0

C ++の経験から、セッター/ゲッターは2つのシナリオに役立ちます。

  1. 文字列や配列などのメンバーに値を割り当てる前に健全性チェックを実行する必要がある場合。
  2. -1(または負の値)がfalseの場合、int値からboolへの割り当てなど、関数のオーバーロードが必要な場合に役立ちます。ゲッターの場合はその逆です。

それとは別に、セキュリティは有効なポイントですが、その重要性は、ログインやdbアクセス関連モジュールのような非常に少数の開発アプリケーションに限定されます。あなたのモジュールを使用する人が少ないのに、なぜ余分なコーディングが必要なのですか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.