クラス設計 vs.IDE:非メンバー非フレンド機能には本当に価値があるのでしょうか?

StackOverflow https://stackoverflow.com/questions/135634

質問

(そうでなければ)素晴らしい本の中で C++ コーディング標準, 、項目 44、タイトル 「非メンバー、非フレンド関数を書くことを好む」, Sutter と Alexandrescu は、クラスのメンバーに本当にアクセスする必要がある関数のみをそのクラスのメンバーにすることを推奨しています。メンバー関数のみを使用して作成できる他のすべての操作は、クラスの一部にするべきではありません。彼らは非会員かつ非友人であるべきです。議論は次のとおりです。

  • クラスの内部にアクセスする必要があるコードが少なくなるため、カプセル化が促進されます。
  • 関数がメンバーであるかどうかを毎回推測する必要がなくなるため、関数テンプレートの作成が容易になります。
  • クラスを小さく保つことで、テストと保守が容易になります。

これらの議論には価値があると思いますが、大きな欠点もあります。 私の IDE ではこれらの関数を見つけることができません。 ある種のオブジェクトがあり、そのオブジェクトに対してどのような操作ができるかを確認したいときは、単に「」と入力することはできません。pMysteriousObject->" そしてメンバー関数のリストを取得できなくなりました。

クリーンなデザインを維持することは、最終的にはプログラミング作業を楽にすることにつながります。しかし、これは実際には私をはるかに困難にするでしょう。

それで、本当に苦労する価値があるのか​​どうか疑問に思っています。 それにはどう対処しますか?

役に立ちましたか?

解決

この件に関しては、私はサッター氏とアレクサンドルスク氏の意見に同意しないことになるだろう。関数の動作がどうかと思います foo() 階級の範囲内にある Barの責任、それでは foo() の一部であるべきです bar().

事実 foo() に直接アクセスする必要はありません Barのメンバー データは、概念的には の一部ではないという意味ではありません。 Bar. 。これは、コードが適切に要素化されていることを意味する場合もあります。他のメンバー関数を介してすべての動作を実行するメンバー関数があることは珍しいことではありませんが、なぜそうすべきなのかわかりません。

周辺機器に関連する機能が必要であることに完全に同意します。 ない クラスの一部である必要がありますが、何かがクラスの責任の中核である場合、それがメンバー データを直接操作しているかどうかに関係なく、それがメンバーであるべきではない理由はありません。

これらの具体的な点については、次のとおりです。

クラスの内部にアクセスする必要があるコードが少なくなるため、カプセル化が促進されます。

実際、内部に直接アクセスする関数は少ないほど良いのです。つまり、メンバー関数を使用すると、可能な限りの機能が実行されます。 経由 他のメンバー関数は良いことです。適切に因数分解された関数をクラスから分割すると、半分のクラスが残るだけで、それを使用するには多数の外部関数が必要になります。適切に因数分解された関数をクラスから引き離すと、適切に因数分解された関数の作成が妨げられるようです。

関数がメンバーであるかどうかを毎回推測する必要がなくなるため、関数テンプレートの作成が容易になります。

これは全く分かりません。クラスから多数の関数を取り出すと、関数テンプレートにより多くの責任が課せられることになります。彼らは次のように仮定することを強いられています 少ないも 機能は、クラスから取得されたほとんどの関数がテンプレートに変換されると仮定しない限り、クラス テンプレート引数によって提供されます (ああ)。

クラスを小さく保つことで、テストと保守が容易になります。

そうですね。また、テストと保守を行うための追加の外部関数も多数作成されます。私にはこれに価値が見当たりません。

他のヒント

スコット・マイヤーズもサッターと同様の意見を持っています。 ここ.

彼は次のようにも明確に述べています。

「さまざまな文字列のようなクラスを使った作業に基づいて、Jack Reeves は、たとえ非フレンドの非メンバーである可能性がある場合でも、一部の関数を非メンバーにすると正しく「感じられない」ことに気づきました。クラスにとって「最適な」インターフェイスは、多くの競合する懸念事項のバランスをとることによってのみ見つけられますが、カプセル化の程度はその 1 つにすぎません。

関数がメンバー関数であることが「理にかなっている」ものである場合は、それをメンバー関数にします。同様に、それが実際にはメインインターフェイスの一部ではなく、非メンバーであることが「理にかなっている」場合は、そうします。

1 つの注意点は、operator==() のオーバーロードされたバージョンでは構文が同じままであることです。したがって、この場合は理由がありません ない 本当にプライベートメンバーにアクセスする必要がある場合を除き、クラスと同じ場所で宣言された非メンバー、非フレンドの浮動関数にします (私の経験では、アクセスすることはほとんどありません)。そして、その場合でも、operator!=() を非メンバーとして、operator==() に関して定義することができます。

彼らの間では、Sutter、Alexandrescu、Meyers が他の誰よりも C++ の品質向上に貢献したと言っても間違いではないと思います。

彼らが尋ねる単純な質問の 1 つは次のとおりです。

ユーティリティ関数にパラメーターとして 2 つの独立したクラスがある場合、どのクラスがメンバー関数を「所有」する必要がありますか?

もう 1 つの問題は、問題のクラスが制御下にある場合にのみメンバー関数を追加できることです。クラス定義を再度開くことができないため、std::string に対して作成するヘルパー関数は非メンバーである必要があります。

これらの両方の例では、IDE が不完全な情報を提供するため、「昔ながらの方法」を使用する必要があります。

世界で最も影響力のある C++ 専門家が、クラス パラメーターを持つ非メンバー関数はクラス インターフェイスの一部であると考えていることを考えると、これはコーディング スタイルというよりも IDE の問題です。

IDE は 1 ~ 2 リリースで変更される可能性があり、この機能を追加してもらうこともできるかもしれません。現在の IDE に合わせてコーディング スタイルを変更すると、将来、拡張不能または保守不能なコードにより大きな問題が発生する可能性があります。

確かに、外部関数はインターフェイスの一部であってはなりません。理論的には、クラスにはデータのみが含まれ、実用的な関数ではなく、目的のインターフェイスを公開する必要があります。ユーティリティ関数をインターフェイスに追加すると、クラスのコード ベースが増大するだけで、保守性が低下します。私は現在、約 50 個のパブリック メソッドを含むクラスを維持していますが、これは非常識です。

さて、実際には、これを強制するのは簡単ではないということに私も同意します。多くの場合、クラスに別のメソッドを追加する方が簡単です。既存のクラスに新しいメソッドを簡単に追加できる IDE を使用している場合はさらに簡単です。

クラスをシンプルに保ちながら外部関数を一元化できるようにするために、私はクラスで動作するユーティリティ クラス、または名前空間をよく使用します。まず、データをラップし、可能な限り単純なインターフェイスを公開するクラスを作成します。次に、クラスで実行するタスクごとに新しいクラスを作成します。

例:Point クラスを作成し、それをビットマップに描画するための PointDrawer クラス、それを保存するための PointSerializer などを追加します。

共通の接頭辞を付けた場合、次のように入力すると IDE が役立つかもしれません。

::prefix

または

namespace::prefix

多くの OOP 言語では、非フレンド、非クラス メソッドは、何にも接続されていない孤児院に住む三級市民です。私がメソッドを書くとき、私は良い親、つまりふさわしいクラスを選びたいと思っています。そこでは、彼らが歓迎され、助けられていると感じられる最高のチャンスがあります。

IDE が実際に助けてくれていると思っていたでしょう。

IDE を隠しています 保護された機能 は利用できないため、リストから除外されます。 公共 クラスの設計者の意図どおりです。

クラスのスコープ内にいて次のように入力した場合 これ-> 保護された機能がリストに表示されます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top