6

効果的な C++ 状態の項目 23: メンバー関数よりも非メンバー非フレンド関数を優先します。

このアイテムの全体的な目的は、パッケージの柔軟性と機能拡張性だけでなく、カプセル化を促進することでしたが、私の質問は、このアドバイスをどこまで受け入れるかということです。

たとえば、クラス、プライベート データ メンバーを用意し、パブリック関数をプライベート データ メンバーのアクセサーやミューテーターのみに減らすことで、最小限のアプローチを取ることができます。次に、他のすべての関数が非メンバー関数になる可能性があります。

しかし、アクセサーやミューテーターがいたるところにあるため、コードの明瞭性が犠牲になる可能性がありますが、カプセル化を増やしてもよろしいですか? 線引きはどこに?

4

5 に答える 5

9

まず、誰もがこのアドバイスに同意するわけではありません。Meyers(編集:Herb Sutter)以外の誰かがこのアドバイスをしているのを見たことがないと思います。そして、C++のコンテキスト内でのみ与えられたのを見たことがあります。たとえば、JavaとC#には無料の関数がないため、JavaまたはC#で「非メンバーの非フレンド関数」を作成することは実際には不可能です。Ruby開発者は、たとえば、意図的にメンバー関数を作成する「人道的なインターフェイス」を好みます。非メンバーができるのと同じことをしてください。ただ、それらの関数の呼び出し元の生活を楽にするためです。

そして、マイヤーズのアドバイスを受け入れたとしても、メンバー関数よりも非メンバー非フレンド関数を優先する必要があります(そしてそれは良いアドバイスだと思いますが、クラスの実装をそのメンバーからでもカプセル化することを考えると、カプセル化をより適切に適用するのに確かに役立ちました関数)、それは考慮すべき設計の唯一の軸です。

オブジェクト指向設計の重要な概念は、オブジェクトが何かをするということです。オブジェクトは、他のコードが処理するセッターとゲッターの単なるバッグではありません。代わりに、動作をアタッチする必要があります。つまり、動作を実行するメソッドを設定する必要があります。また、それらの動作の詳細をカプセル化する必要があります。OOへのこのアプローチに従う場合、マイヤーズのアドバイスを極端に実行すると、カプセル化を支援するのではなく、カプセル化が損なわれます。クラスの内部実装変数を非表示にするのではなく、ゲッターとセッターを介して公開することになります。メソッド(クラスに代わって処理を行うコード。これが、クラスを最初に作成する唯一の理由です)がそれに到達できます。

したがって、マイヤーズのアドバイスをどこまで取るかという質問に答えるには、クラスのパブリックインターフェイスを使用して非フレンド非メンバー関数として合理的に実装できる場合は、関数をメンバー関数に不必要に変換しないでください。ただし、クラスのパブリックインターフェイスを損傷しないでください。パブリックインターフェイスであり、何かをメンバーにすること避けるために実装を公開することにより、カプセル化に違反します。また、カプセル化と他の懸念事項や他のアプローチとのバランスをとるようにしてください(チームがそのルートを選択する場合は、本格的な人道的インターフェースの長所と短所を含みます)。

于 2009-09-29T02:48:52.020 に答える
3

一歩下がって、クラスの目的を考えてみましょう。それはどのよう仕事をしているのでしょうか。その1つの仕事を最適に行うために、どのクラスの不変条件を確保する必要がありますか?クラスの目的において、サブクラス化とオーバーライドはどのような役割を果たしますか?

アクセサーとミューテーターの観点からすべてをキャストすることは絶対に適切ではありません。これは、OOPのルートにある状態と動作の結合をほぼ切り離すか、問題の適切なフレーミングではない属性の取得または設定に関する動作をマスクします。そのような「ふりをした」ミューテーターとアクセサーのベール。

アクセサーとミューテーターだけのクラスは、1つの特殊なケースです。おそらくstruct、いくつかの不変条件を保持できることにより、従来のCの種類から一歩進んだものですが、「かろうじて」;-)。

優れたOOP設計のほとんどのクラスには動作があります。ジェネリックプログラミングが好きなのと同じように、C ++を使用する理由の1つは、複数のパラダイムが強力に混在していることです。

于 2009-09-29T02:47:14.113 に答える
2

実際、クラスのプライベート変数にアクセサーとミューテーターのみを提供することは、実際にはミニマリストではありません(または、ある意味ではミニマリストですが、「最も関連性のある」意味ではありません。カプセル化の考え方は、クライアントコードがジョブを実行できるようにしながら、クラスのインターフェイスを可能な限り制限する必要があるということです。

このアプローチに従うと、将来の基盤となる実装の変更が容易になります。これが、そもそもカプセル化のポイントです。

于 2009-09-29T02:47:27.200 に答える
0

物事を適切なサイズのユニットにパッケージ化する方法に取り組むことは、主観的な芸術です。

非メンバーを非フレンドにする際に私が抱えている問題の 1 つは、内部構造の変更により、既存の非メンバーをサポートするために新しいパブリック インターフェイスを追加する必要がある場合、または非メンバーがフレンドとして認識されるようになった場合です。以前に考案されたものよりも緊密なリンケージを示しています。現在、変更を加えるにはコストがかかる可能性があります。

論理レベルでは、非メンバー関数をクラスにパッケージ化してクラスのインターフェイスを構成できると言えますが、メソッドの場所が変更された場合、開発者はコードを変更する必要があります。インターフェイスは、エラー、その使用において透過的ではありません。

Eiffel の優れた点の 1 つは、パラメーターのないメソッドが変数と同じ方法でアクセスされるため、これらの要素間の変更が透過的であることです。

于 2009-09-29T03:12:32.837 に答える
0

一般に、アクセサーとミューテーターの背後にある考え方は、変数を何らかの方法で保護する必要があるというものです。変数に、1 つのエンティティのみによって更新されるはずの機密情報が含まれている場合は、ミューテーターにそのチェックを含めることができます。単純な事実として、ほとんどのアクセサーとミューテーターは単なる形式的なものであり (C# が自動プロパティを持つようになったため)、必要ではありません。肝心なのは、あなたの判断を使用してください。アクセスを制限する必要がある場合は、ミューテーターを追加します。誰が変数を取得/設定するかが重要でない場合、アクセサーとミューテーターは必要ありません。

于 2009-09-29T02:34:22.307 に答える