20

3D 空間での位置を表す非常に単純なクラスを作成しています。

現在、ユーザーが個々のXYおよびZ値に直接アクセスして変更できるようにしています。つまり、パブリック メンバー変数です。

template <typename NumericType = double>
struct Position
{
    NumericType X, Y, Z;

    // Constructors, operators and stuff...
};

これの背後にある理由NumericTypeは、 はテンプレート パラメーターであるため、値の健全性をチェックするための適切な方法があるとは言えません。(ユーザーが位置を負の値で表現することを望まないことをどのように知ることができますか?) したがって、ゲッターやセッターを追加してインターフェイスを複雑にする意味はなく、簡潔さのために直接アクセスを優先する必要があります。

Pos.X = Pos.Y + Pos.Z; // Versus...
Pos.SetX(Pos.GetY() + Pos.GetZ());

これは良い慣行の例外ですか?私のコードの(仮想の)将来のメンテナーは私を追い詰めて顔を殴りますか?

4

6 に答える 6

16

ゲッターとセッターを使用する背後にある考え方は、値を設定するだけでなく、他の動作を実行できるようにすることです。クラスに後付けしたいものが多数あるため、この方法をお勧めします。

セッターを使用する一般的な理由 (おそらく他にもあります):

  • 検証: 変数の型で許可されているすべての値がメンバーに対して有効であるとは限りません: 代入の前に検証が必要です。
  • 不変条件: 依存フィールドを調整する必要がある場合があります (たとえば、配列のサイズを変更するには、新しいサイズを格納するだけでなく、再割り当てが必要になる場合があります)。
  • フック: 通知のトリガーなど、割り当ての前後に実行する追加の作業があります (例: オブザーバー/リスナーが値に登録されます)。
  • 表現: フィールドは、getter および setter として「公開」形式で保存されません。フィールドはオブジェクト自体に格納されていない場合もあります。値は他の内部メンバーに転送されるか、別のコンポーネントに格納される可能性があります。

コードで上記のいずれかを使用したり、要求したりすることは絶対にないと思われる場合は、原則としてゲッターとセッターを作成することは間違いなく適切な方法ではありません。コードが肥大化するだけです。

編集: 一般に信じられていることとは反対に、ゲッターとセッターを使用しても、これらの変更が軽微でない限り、クラスの内部表現を変更するのに役立つ可能性はほとんどありません。特に、個々のメンバーに対するセッターの存在は、この変更を非常に困難にします。

于 2011-06-29T23:48:20.947 に答える
3

ゲッターとセッターは、さまざまな方法で実装した抽象値を取得/設定する場合にのみ、重要な設計上の選択です。しかし、クラスが非常に単純で、データ メンバーが非常に基本的であり、直接公開する必要がある場合は、それらを公開してください。フリルのない素敵で安価な集約型が得られ、それは完全に自己文書化されています。

本当にデータ メンバーを非公開にしたいが、それでも完全にアクセスできるようにしたい場合は、単一のアクセサー関数を 1 回だけ asT & access()と 1 回だけオーバーロードしconst T & access() constます。


編集:最近のプロジェクトでは、グローバルアクセサー関数を使用して、座標にタプルを使用しました。おそらくこれは役に立つかもしれません:

template <typename T>
inline T cX(const std::tuple<T,T,T> & t) { return std::get<0>(t); }

typedef std::tuple<double, double, double> coords;
//template <typename T> using coords = std::tuple<T,T,T>; // if I had GCC 4.8

coords c{1.2, -3.4, 5.6};

// Now we can access cX(c), cY(c), cZ(c).
于 2011-06-29T23:58:12.333 に答える
2

しばらく時間がかかりましたが、この古い Stroustrup のインタビューをたどりました。そこで彼は、公開されたデータ構造体とカプセル化されたクラスについて議論しています: http://www.artima.com/intv/goldilocks3.html

詳細をさらに詳しく説明すると、既存の回答に欠けている/過小評価されている可能性のある側面があります。カプセル化の利点は、次の場合に増加します。

  • 再コンパイル/リンクの依存関係: 多数のアプリケーションで使用される低レベルのライブラリ コードで、これらのアプリケーションの再コンパイルと再デプロイに時間がかかる、または困難な場合があります。
    • 通常、実装が標準外の場合 (pImpl のイディオムとパフォーマンスの妥協が必要になる場合があります) の方が簡単なので、再リンクするだけで済みます。また、新しい共有ライブラリをデプロイしてアプリを単純にバウンスできる場合はさらに簡単です。
    • extern対照的に、オブジェクトが特定の翻訳単位の「非」実装でのみ使用される場合、カプセル化によるメリットは大幅に少なくなります。
  • 実装の不安定性にもかかわらずインターフェイスの安定性: 実装がより実験的/揮発性であるが、API 要件がよく理解されているコード
    • 注意することtypedefで、型に s を使用しているときにメンバー変数に直接アクセスできる可能性があることに注意してください。これにより、プロキシ オブジェクトを代用して、異なる実装を呼び出しながら同一のクライアント コードの使用をサポートできます。
于 2011-06-30T02:09:10.550 に答える
0

非常に簡単なことをすれば、解決策はうまくいくかもしれません。

後で、球座標系での計算がはるかに簡単または高速であることに気付いた場合 (そしてパフォーマンスが必要な場合) は、そのパンチに頼ることができます。

于 2011-06-29T23:47:00.993 に答える
0

これは、NumericType がテンプレート パラメーターであるため、値が正常かどうかを確認する適切な方法があるとは言えません。(ユーザーが位置を負の値で表したくないと思うのはどうすればわかりますか?)

言語とコンパイラは、このケースを (特殊化によって) サポートしています。

したがって、getter や setter を追加してインターフェイスを複雑にする意味はなく、簡潔にするために直接アクセスを優先する必要があります。

根拠のない議論 - 上記を参照してください。

これは良い慣行の例外ですか?

そうではないと思います。あなたの質問は、検証template存在する必要があることを意味しますが、実装で a を使用することを選択し、選択した言語機能に適切に特化していないため、実装/サポートする価値はありません。そのアプローチでは、インターフェイスは部分的にのみサポートされているように見えます。これらの欠落した実装は、クライアントの実装を汚染するだけです。

于 2012-04-22T05:42:27.630 に答える
0

次のようなよく知られた構造については問題ありません。

  1. int のように、任意の値を持つことができます。
  2. パフォーマンス上の理由から、値を操作するときは組み込み型のように動作する必要があります。

ただし、「単なる 3D ベクトル」以上の型が必要な場合は、プライベート メンバーとして別のクラスにラップする必要があります。これにより、メンバー関数と追加機能のメンバー関数を介して x、y、および z が公開されます。

于 2011-06-29T23:46:43.963 に答える