51

アレン・ホルブは次のように書いています。

何らかの結合なしにプログラムを作成することはできません。それにもかかわらず、オブジェクト指向 (オブジェクト指向) の原則に従うことで、結合を大幅に最小限に抑えることができます (最も重要なのは、オブジェクトの実装を、それを使用するオブジェクトから完全に隠す必要があるということです)。たとえば、オブジェクトのインスタンス変数 (定数ではないメンバー フィールド) は、常に非公開にする必要があります。限目。例外なく。これまで。私は真剣です。(保護されたメソッドを効果的に使用できる場合もありますが、保護されたインスタンス変数は忌まわしいものです。)

それは理にかなっているように聞こえますが、彼は続けて次のように言います。

同じ理由で、 get/set 関数を使用しないでください。それらは、フィールドを public にする方法が非常に複雑であるためです (ただし、基本型の値ではなく本格的なオブジェクトを返すアクセス関数は、返されたオブジェクトがクラスは設計における重要な抽象化です)。

率直に言って、これは私には非常識に聞こえます。

情報隠蔽の原則は理解できますが、アクセサーとミューテーターがなければ、Java Bean をまったく使用できません。モデルはビューのレンダリングを担当できないため、モデルにアクセサーなしで MVC 設計に従う方法がわかりません。

しかし、私は若いプログラマーであり、オブジェクト指向設計について日々学んでいます。おそらく、経験豊富な人がこの問題に加担できるでしょう。

Allen Holub の参考記事


関連する質問:

4

13 に答える 13

40

Holub が、通常はオブジェクトの状態を変更することは避け、代わりに統合されたメソッド (動作の実行) を使用してこの目的を達成する必要があると言っていることに問題はありません。Corletk が指摘しているように、最高レベルの抽象化について長く真剣に考えることには知恵があり、カプセル化のエンドランを可能にするゲッター/セッターを使って軽率にプログラミングするだけではありません。

しかし、セッターを「絶対に」使用してはならない、またはプリミティブ型に「決して」アクセスしてはならないという人には、私は非常に苦労しています。実際、すべてのケースでこのレベルの純度を維持するために必要な労力は、適切に実装されたプロパティを使用するよりもコードを複雑にする可能性があり、最終的にはコードが複雑になる可能性があります。長期的な痛みを犠牲にして短期的な利益を得るためのルールを回避しているときを知るのに十分な感覚が必要です.

ホルブはあなたが違いを知っているとは信じていません。違いを知ることがプロになると思います。

于 2009-06-15T15:08:56.197 に答える
33

その記事を注意深く読んでください。Holubは、ゲッターとセッターが邪悪な「デフォルトのアンチパターン」であるという点を強調しています。これは、システムを設計するときに私たちが陥る悪い習慣です。できるからです。

思考プロセスは線に沿っている必要があります。このオブジェクトは何をします?その責任は何ですか?その振る舞いは何ですか?それは何を知っていますか?これらの質問についてじっくり考えてみると、可能な限り最高レベルのインターフェースを公開するクラスを設計することに自然につながります。

車は良い例です。明確に定義され、標準化された高レベルのインターフェースを公開します。私は気にしませんsetSpeed(60)...それはMPHですか、それともkm / hですか?私はただ加速し、巡航し、減速します。の詳細について考える必要はありませんsetSteeringWheelAngle(getSteeringWheelAngle()+Math.rad(-1.5))、私はただturn(-1.5)、詳細は内部で処理されます

つまり、「すべてのクラスが何に使用されるのか、何をするのか、何を表すのかを理解し、それらの要件を満たす可能な限り最高レベルのインターフェースを公開することができ、またそうすべきです。ゲッターとセッターは通常、コップアウトです。プログラマーは、各クラスが何であるかを正確に判断するために必要な分析を怠惰に行うだけなので、「何でもできる」という道をたどります。ゲッターとセッターは悪です!

クラスの実際の要件が事前にわからない場合があります。かっこいいです。今のところ、ゲッター/セッターのアンチパターンを使用してください。しかし、経験を通じて、クラスが何に使用されているかを知っている場合は、戻ってきて、汚れた低レベルのインターフェイスをクリーンアップすることをお勧めします。「そもそも吸盤を書くときに知っておきたいこと」に基づいたリファクタリングは当然のことです。始めるためにすべてを知っている必要はありません。知っているほど、途中で必要な手直しが少なくなる可能性があります。

それが彼が推進している精神です。ゲッターとセッターは簡単に陥りやすいトラップです。

はい、豆は基本的にゲッターとセッターを必要としますが、私にとって豆は特別な場合です。Beanは、名詞、物、具体的な識別可能な(物理的でない場合)オブジェクトを表します。実際に自動動作するオブジェクトは多くありません。ほとんどの場合、物事は人間を含む外力によって操作され、生産的なものになります。

daisy.setColor(Color.PINK)完全に理にかなっています。他に何ができますか?バルカンの心が溶けて、花をピンクにしたいのではないでしょうか。うーん?

ゲッターとセッターには悪がありますか?場所。ただ、すべての本当に良いOOのものと同様に、私たちはそれらを使いすぎる傾向があります。なぜなら、それらは安全で親しみやすく、単純なことは言うまでもなく、したがって、少なくとも彼らがdは心を溶かすものをマスターしました。

于 2009-06-15T14:20:53.520 に答える
31

この記事で言い換えると、アレン・ホーラブが言おうとしたことは次のとおりだと思います。

ゲッターとセッターは、特にカプセル化する変数に役立ちますが、すべての変数に使用する必要はありません。実際、すべての変数にそれらを使用することは、厄介なコードの臭いです。

プログラマーが抱えている問題、そしてAllen Holubがそれを指摘するのに正しかったのは、すべての変数にゲッター/セッターを使用することがあるということです。そして、カプセル化の目的は失われます。

于 2009-06-15T13:48:25.093 に答える
10

(私は.NETの「プロパティ」の角度からこれに来ていることに注意してください)

まあ、単純に-私は彼に同意しません。彼は、プロパティの戻り型が呼び出し元のコードを壊す可能性があるため、悪いことであることに大騒ぎしますが、メソッドの引数にもまったく同じ引数が適用されます。そして、あなたもメソッドを使用できない場合はどうなりますか?

OK、メソッド引数は変換を拡大するように変更できますが、....理由は...また、C#では、varキーワードがこの認識された苦痛の多くを軽減できることに注意してください。

アクセサーは実装の詳細ではありません。それらはパブリックAPI/コントラクトです。うん、あなたがcontracftを壊すならば、あなたは問題を抱えています。それが驚きになったのはいつですか?同様に、アクセサが重要であるのは珍しいことではありません。つまり、アクセサは単にフィールドをラップするだけではありません。それらは、計算、論理チェック、通知などを実行します。また、インターフェースベースの状態の抽象化を可能にします。ああ、そしてポリモーフィズム-など。

アクセサーの冗長な性質を再確認してください(p3?4?)-C#の場合:public int Foo {get; private set;}-作業は完了しました。

最終的に、すべてのコードは、コンパイラーに対する私たちの意図を表現する手段です。プロパティを使用すると、タイプセーフ、コントラクトベース、検証可能、拡張可能、ポリモーフィックな方法でそれを実行できます。ありがとうございます。なぜこれを「修正」する必要があるのですか?

于 2009-06-15T13:49:20.617 に答える
9

ゲッターとセッターは、プライベート変数をパブリックにするためのマスクとしてのみ使用されます。

Holubがすでに言ったことを繰り返す意味はありませんが、その核心は、クラスは状態だけでなく動作を表す必要があるということです。

于 2009-06-15T13:49:15.810 に答える
8

一部の反対意見はイタリック体で示しています。

getIdentity は「get」で始まりますが、フィールドを返すだけではないため、アクセサーではありません。合理的な動作をする複雑なオブジェクトを返します

ああ、でも待ってください...では、プリミティブ型の代わりにオブジェクトを返す限り、アクセサーを使用しても問題ありませんか? それは別の話ですが、それは私にとって同じようにばかげています。オブジェクトが必要な場合もあれば、プリミティブ型が必要な場合もあります。

また、アレンは、同じトピックに関する以前のコラムで、「アクセサーを使用しない」というマントラが 1 つの例外も受けなかったので、彼の立場を根本的に和らげていることに気付きました。結局のところ、彼は数年後にアクセサーが目的を果たしていることに気付いたのかもしれません...

ビジネス ロジックに実際に UI コードを入れていないことに注意してください。AWT (Abstract Window Toolkit) または Swing の観点から UI レイヤーを作成しましたが、どちらも抽象化レイヤーです。

良いもの。SWT でアプリケーションを作成している場合はどうなるでしょうか。その場合、実際にAWTはどの程度「抽象的」ですか? ただ、このアドバイスは、ビジネス ロジックで UI コードを記述することにつながるだけです。なんて素晴らしい原則でしょう。結局のところ、このプラクティスがプロジェクトで行うことができる最悪の設計上の決定の 1 つであることを私たちが特定してから、少なくとも 10 年ほどしか経っていません。

私の問題は、初心者のプログラマーが時々インターネット上の記事に出くわし、私よりも多くの信用を与えることです。おそらく、これはそれらのケースの1つです。

于 2009-06-15T13:56:02.753 に答える
5

このようなアイデアが提示されると、私が使用しているライブラリとフレームワークを見て、使用するのが好きです。

たとえば、同意しない人もいますが、私は Java 標準 API が好きです。また、Spring Framework も気に入っています。これらのライブラリのクラスを見ると、内部変数を公開するためだけに存在するセッターとゲッターがほとんどないことに気付くでしょう。getXという名前のメソッドがありますが、それは従来の意味での getter という意味ではありません。

Eclipse (またはお好みの IDE) で「ゲッター/セッターの生成」を選択するたびに、一歩下がって、自分が何をしているのか疑問に思う必要があります。この内部表現を公開することは本当に適切でしょうか? それとも、ある段階でデザインを台無しにしてしまったのでしょうか?

于 2010-03-04T20:57:07.643 に答える
2

ゲッター/セッターの問題は、カプセル化を偽造しようとすることですが、実際には内部を公開することでカプセル化を破ります。第二に、彼らは2つの別々のことをしようとしています-彼らの状態へのアクセスを提供し、制御する-そしてどちらもうまくやっていないことになります。

get / setメソッドを呼び出すときは、最初に変更するフィールドの名前を知っている(または良いアイデアを持っている)必要があり、次にそのタイプを知っている必要があるため、カプセル化が壊れます。あなたは呼び出すことができませんでした

setPositionX("some string");

フィールドの名前とタイプがわかっていて、セッターがパブリックである場合は、とにかく誰でもメソッドをパブリックフィールドであるかのように呼び出すことができます。これは、より複雑な方法なので、単純化して作成します。そもそもパブリックフィールドです。

その状態へのアクセスを許可すると同時にそれを制御しようとすると、get / setメソッドは物事を混乱させ、無駄なボイラープレートになるか、誤解を招くことになります。ユーザーが期待しないかもしれない効果。エラーチェックが必要な場合は、次のように呼び出すことができます

public void tryPositionX(int x) throws InvalidParameterException{
    if (x >= 0)
        this.x = x;
    else
        throw new InvalidParameterException("Holy Negatives Batman!");
}

または、追加のコードが必要な場合は、メソッド全体の動作に基づいて、より正確な名前を呼び出すことができます。

tryPositionXAndLog(int x) throws InvalidParameterException{
    tryPositionX(x);
    numChanges++;
}

何かを機能させるためにゲッター/セッターを必要とする私見は、しばしば悪いデザインの症状です。「伝える、聞かない」の原則を利用するか、オブジェクトが最初に状態データを送信する必要がある理由を再考してください。オブジェクトの状態ではなく、オブジェクトの動作を変更するメソッドを公開します。その利点には、メンテナンスの容易さと拡張性の向上が含まれます。

あなたはMVCについても言及し、モデルはそのビューに責任を負わないと言います。その場合、Allen Holubは、「give-me-a-JComponent-that-represents-your-identityクラス」を使用して抽象化レイヤーを作成する例を示します。 「彼は、「アイデンティティがシステムの他の部分から表現される方法を分離する」と述べています。私はそれがうまくいくかどうかについてコメントするのに十分な経験はありませんが、表面的にはそれはまともな考えに聞こえます。

于 2011-10-08T22:05:25.473 に答える
2

パブリック変数は、クラスが実際の一貫性を持たないデータの束にすぎない場合、または本当に基本的なもの (ポイント クラスなど) の場合に意味があります。一般に、おそらく public であってはならないと思われる変数がクラスにある場合、それはそのクラスにある程度の一貫性があり、変数には維持する必要がある特定の関係があることを意味します。そのため、すべての変数を private にする必要があります。

ゲッターとセッターは、ある種の首尾一貫したアイデアを反映している場合に意味があります。たとえば、ポリゴン クラスでは、指定された頂点の x 座標と y 座標は、クラス境界の外側で意味を持ちます。ゲッターを持つことはおそらく理にかなっており、セッターを持つことはおそらく理にかなっています。銀行口座クラスでは、残高はおそらくプライベート変数として格納され、ほぼ確実にゲッターが必要です。セッターがある場合は、監査可能性を維持するためにロギングを組み込む必要があります。

パブリック変数よりもゲッターとセッターにはいくつかの利点があります。それらは、インターフェースと実装のいくつかの分離を提供します。ポイントに.getX()関数があるからといって、x が必要であるとは限りません。なぜなら、x は.getX()半径.setX()座標で問題なく動作するようにすることができるからです。もう 1 つは、setter 内でクラスの一貫性を維持するために必要なことは何でも行うことで、クラスの不変条件を維持できることです。もう 1 つは、銀行口座残高のログ記録など、セットでトリガーされる機能を持つことができることです。

ただし、より抽象的なクラスの場合、メンバー変数は個々の重要性を失い、コンテキスト内でのみ意味を持ちます。たとえば、C++ ストリーム クラスのすべての内部変数を知る必要はありません。要素を出し入れする方法、およびその他のさまざまなアクションを実行する方法を知る必要があります。正確な内部構造を当てにすると、コンパイラやバージョン間で任意に異なる可能性がある詳細に行き詰まります。

したがって、プライベート変数は、オブジェクトの動作に実際の意味を持つゲッターとセッターをほぼ排他的に使用し、それ以外の場合は使用しないと言えます。

ゲッターとセッターが頻繁に使用されているからといって、それらが役に立たないというわけではありません。

于 2009-06-15T15:33:44.687 に答える
2

get/set を絶対に使用しないと彼が言っているとは思いませんが、フィールドに get/set を使用することは、単にフィールドを公開することよりも優れているとは言えません (たとえば、public string Name と public string Name {get; set; })。

get/set を使用すると、OO の情報隠蔽が制限され、不適切なインターフェイスに閉じ込められる可能性があります。

上記の例では、Name は文字列です。後でデザインを変更して複数の名前を追加したい場合はどうすればよいですか? インターフェイスは単一の文字列しか公開していないため、既存の実装を壊さずに追加することはできません。

ただし、get/set を使用する代わりに、最初に Add(string name) などのメソッドがあった場合、内部的に名前を単独で処理したり、リストに追加したり、そうでないものを追加したり、追加したい回数だけ Add メソッドを外部から呼び出したりできます。より多くの名前。

オブジェクト指向の目標は、抽象化のレベルで設計することです。必要以上に詳細を公開しないでください。

プリミティブ型を get/set でラップしただけの場合、この原則を破っている可能性があります。

もちろん、これは OO の目標を信じている場合です。ほとんどの場合、機能コードをグループ化するための便利な方法としてオブジェクトを使用しているだけです。

于 2009-06-15T14:13:54.957 に答える
1

public getter/setter は、実装の詳細へのアクセスを提供する場合は不適切です。それでも、オブジェクトのプロパティへのアクセスを提供し、getter/setter を使用することは合理的です。たとえば、Car に color プロパティがある場合、getter を使用してクライアントに「観察」させることができます。一部のクライアントが車の色を変更する機能を必要とする場合、クラスはセッターを提供できます (ただし、'recolor' の方が明確な名前です)。プロパティがオブジェクトにどのように格納されているか、どのように維持されているかなどをクライアントに知らせないことが重要です。

于 2010-03-04T20:22:56.807 に答える
0

うーん...彼はカプセル化の概念について聞いたことがありません。クラスのメンバーへのアクセスを制御するために、GetterメソッドとSetterメソッドが導入されています。すべてのフィールドを公開することで...誰でも好きな値を書き込むことができ、それによってオブジェクト全体が完全に無効になります。

誰かがカプセル化の概念について少し曖昧な場合に備えて、ここでそれを読んでください:

カプセル化(コンピュータサイエンス)

...そして、それらが本当に悪である場合、.NETはプロパティの概念を言語に組み込みますか?(少しきれいに見えるGetterおよびSetterメソッド)

編集

この記事では、カプセル化について言及しています。

ゲッターとセッターは、特にカプセル化したい変数に役立ちますが、すべての変数に使用する必要はありません。実際、すべての変数に使用するのは厄介なコードの臭いです。

この方法を使用すると、長期的にコードを維持するのが非常に困難になります。プロジェクトの途中でフィールドをカプセル化する必要があることがわかった場合は、ソフトウェア内のあらゆる場所でそのフィールドのすべての参照を更新して、メリットを得る必要があります。適切なカプセル化を前もって使用し、後で頭痛を防ぐ方がはるかに賢く聞こえます。

于 2009-06-15T13:47:54.580 に答える
0

ゲッターとセッターは、クラス外でアクセスまたは変更する必要がある変数にのみ使用する必要があると思います。そうは言っても、静的でない限り、変数をパブリックにする必要はないと思います。これは、静的ではない変数をパブリックにすると、望ましくない変更が行われる可能性があるためです。不注意にパブリック変数を使用している開発者がいるとします。次に、別のクラスの変数にアクセスし、意味もなく変更します。この事故の結果、彼のソフトウェアにエラーが発生しました。そういうわけで、私はゲッターとセッターの適切な使用を信じていますが、すべてのプライベート変数またはプロテクト変数に必要というわけではありません。

于 2009-06-19T06:05:24.597 に答える