C ++のすべてにオブジェクト(プリミティブ型ではなく)を使用するのは理にかなっていますか?


17

私が取り組んでいる最近のプロジェクトでは、次のような多くの関数を使用する必要がありました。

static bool getGPS(double plane_latitude, double plane_longitude, double plane_altitude,
                       double plane_roll, double plane_pitch, double plane_heading,
                       double gimbal_roll, double gimbal_pitch, double gimbal_yaw, 
                       int target_x, int target_y, double zoom,
                       int image_width_pixels, int image_height_pixels,
                       double & Target_Latitude, double & Target_Longitude, double & Target_Height);

だから私はこのように見えるようにそれをリファクタリングしたい:

static GPSCoordinate getGPS(GPSCoordinate plane, Angle3D planeAngle, Angle3D gimbalAngle,
                            PixelCoordinate target, ZoomLevel zoom, PixelSize imageSize)

これは、最初の方法よりもはるかに読みやすく安全であるように思えます。しかし、作成PixelCoordinateしてPixelSizeクラスを作成するのは理にかなっていますか?またはstd::pair<int,int>、それぞれに使用する方が良いでしょう。そして、ZoomLevelクラスを持つことは理にかなっていdoubleますか、または単に使用する必要がありますか?

すべてにクラスを使用する背後にある私の直感は、これらの仮定に基づいています。

  1. すべてのクラスがある場合ZoomLevelWeightオブジェクトが予期される場所にを渡すことは不可能であるため、関数に間違った引数を提供することはより困難になります。
  2. 同様に、一部の違法な操作は、a GPSCoordinateZoomLevel別のaGPSCoordinate
  3. 法的業務は表現しやすく、タイプセーフです。つまり、2を引くGPSCoordinateと、GPSDisplacement

しかし、私が見たほとんどのC ++コードは多くのプリミティブ型を使用しており、そのための十分な理由があるに違いないと思います。オブジェクトを何かに使用するのは良い考えですか、それとも私が知らない欠点がありますか?


8
「私が見たほとんどのC ++コードは多くのプリミティブ型を使用します」-2つの考えが思い浮かびます。また、スタージョンの法則
congusbongus

3
そして、それはプリミティブ型がシンプル、簡単、高速、洗練され、効率的だからだと思います。OPの例では、新しいクラスをいくつか導入することをお勧めします。しかし、タイトルの質問(「すべて」と書かれている)への答えは圧倒的です。
ミスターリスター

1
@Maxでdoubleを型定義しても、OPの問題を解決することはまったくありません。
ミスターリスター

1
@CongXu:私はほとんど同じ考えを持っていましたが、「抽象化の構築にまったく問題があるプログラマーによって、どの言語の多くのコードが書かれていたかもしれない」という意味です。
ドックブラウン

1
ことわざにあるように、「17個のパラメーターを持つ関数がある場合、おそらくいくつかを見落としていました」。
ジョリスティマーマンズ

回答:


24

はい、間違いなく。あまりにも多くの引数をとる関数/メソッドはコードsmellであり、次の少なくとも1つを示します。

  1. 関数/メソッドは一度に多くのことを行っています
  2. 関数/メソッドはオブジェクト指向設計法を求めている、伝えていない、または違反しているので、その多くのものにアクセスする必要があります
  3. 引数は実際に密接に関連しています

最後のケースが当てはまる場合(そして、あなたの例が確かにそう示唆している場合)、ほんの数十年前に、クールな子供たちがおっしゃっていた派手なパンツの「抽象化」を行う時が来ました。実際、私はあなたの例よりもさらに進んで、次のようなことをします。

static GPSCoordinate getGPS(Plane plane, Angle3D gimbalAngle, GPSView view)

(私はGPSに精通していないので、これはおそらく正しい抽象化ではありません。たとえば、と呼ばれる関数でズームが何をするのか理解できませんgetGPS。)

2つのデータが同じであっても、PixelCoordinateover std::pair<T, T>などのクラスを優先してください。セマンティックな値に加えて、追加の型安全性が少し追加されます(コンパイラーは、両方ともsであってもa ScreenCoordinateに渡すのを停止します)。PixelCoordinatepair


1
言うまでもなく、すべてのクールな子供たちがやろうとしている小さな最適化のために、参照によって大きな構造体を渡すことができますが、実際にはすべきではありません
ラチェットフリーク

6

免責事項:私はC ++よりもCを好む

クラスの代わりに、構造体を使用します。構造体とクラスはほぼ同じなので、この点はせいぜい教訓的ですが(単純なチャートまたはこのSOの質問についてはこちらを参照)、差別化にはメリットがあると思います。

Cプログラマーは、グループ化するとより大きな意味を持つデータのブロックとして構造体に精通しますが、クラスを見ると、内部状態とその状態を操作するメソッドがあると思います。GPS座標には状態がなく、データだけがあります。

あなたの質問から、これはまさにあなたが探しているものであり、一般的なコンテナであり、ステートフルな構造ではないようです。他の人が述べたように、私はZoomを独自のクラスに入れることを気にしません。私は個人的に、データ型を保持するために構築されたクラスが嫌いです(私の教授は作成HeightIdクラスが大好きです)。

すべてのクラスがある場合、Weightオブジェクトが期待される場所にZoomLevelを渡すことは不可能であるため、関数に間違った引数を提供することはより困難になります。

これは問題だとは思わない。明らかなことをグループ化してパラメーターの数を減らした場合、ヘッダーはそれらの問題を防ぐのに十分なはずです。本当に心配な場合は、プリミティブを構造体で分離します。これにより、よくある間違いのコンパイルエラーが発生します。2つのパラメーターを簡単に入れ替えることができますが、離れていると難しくなります。

同様に、GPSCoordinateをZoomLevelまたは別のGPSCoordinateに追加するなど、一部の違法な操作によりコンパイルエラーが発生します。

演算子のオーバーロードの適切な使用。

法的業務は表現しやすく、タイプセーフです。つまり、2つのGPSCoordinatesを引くと、GPSDisplacementが生成されます。

演算子のオーバーロードも適切に使用します。

あなたは正しい軌道に乗っていると思います。考慮すべきもう1つのことは、これがプログラムの残りの部分にどのように適合するかです。


3

専用の型を使用すると、引数の順序を台無しにするのがはるかに困難になるという利点があります。たとえば、サンプル関数の最初のバージョンは、9つのdoubleのチェーンで始まります。誰かがこの機能を使用する場合、GPS座標の代わりに順序を間違え、ジンバルの角度を渡すのは非常に簡単です。これにより、非常に奇妙で追跡が困難なバグが発生する可能性があります。代わりに、異なるタイプの3つの引数のみを渡すと、コンパイラはこのエラーをキャッチできます。

実際、さらに一歩進んで、技術的には同じですが意味的な意味が異なる型を導入することを検討します。たとえば、クラスPlaneAngle3dと別個のclass を持つことによりGimbalAngle3d、両方が3つのdoubleのコレクションである場合でも。これは、意図的でない限り、一方を他方として使用することを不可能にします。

typedef double ZoomLevelその理由だけでなく、別の理由でも、プリミティブ型の単なるエイリアスであるtypedefを使用することをお勧めします()。また、後でタイプを簡単に変更できます。ある日、使用するfloatことでdoubleメモリやCPUの効率が向上する可能性があるという考えが浮かんだら、これを数十個ではなく1か所で変更するだけで済みます。


typedefの提案に対して+1。プリミティブ型とオブジェクトの両方の長所を利用できます。
カールビーレフェルト

クラスメンバの型を変更するのはtypedefほど難しくありません。または、プリミティブと比較することを意味しましたか?
MaHuJa

2

すでにここで良い答えがありますが、追加したいことが1つあります。

PixelCoordinateand PixelSizeクラスを使用すると、物事が非常に具体的になります。一般CoordinateまたはPoint2Dクラスがおそらくあなたのニーズに合っているかもしれません。A Point2Dは、最も一般的なポイント/ベクトル演算を実装するクラスでなければなりません。そのようなクラスを一般的に実装することもできます(Point2D<T>Tがそうであるintdouble、他の何かである場合)。

2Dポイント/ベクトル操作は多くの2Dジオメトリプログラミングタスクで非常に一般的であるため、私の経験では、このような決定は既存の2D操作を再利用する可能性を非常に高めます。、「Whatsover」-CoordinateクラスごとPixelCoordinateGPSCoordinate何度も再実装する必要はありません。


1
struct PixelCoordinate:Point2D<int>適切な型の安全性が必要な場合にも行うことができます
ラチェットフリーク

0

だから私はこのように見えるようにそれをリファクタリングしたい:

static GPSCoordinate getGPS(GPSCoordinate plane, Angle3D planeAngle, Angle3D gimbalAngle,
                        PixelCoordinate target, ZoomLevel zoom, PixelSize imageSize)

これは、最初の方法よりもはるかに読みやすく安全であるように思えます。

[もっと読みやすい]。また、良いアイデアです。

しかし、PixelCoordinateクラスとPixelSizeクラスを作成するのは理にかなっていますか?

それらが異なる概念を表す場合、それは理にかなっています(つまり、「はい」)。

または、それぞれにstd :: pairを使用する方が良いでしょう。

を開始することができtypedef std::pair<int,int> PixelCoordinate, PixelSize;ます。この方法で、セマンティクスをカバーし(クライアントコードのstd :: pairsではなく、ピクセル座標とピクセルサイズを使用します)、拡張する必要がある場合は、typedefをクラスに置き換えるだけで、クライアントコードは最小限の変更が必要です。

そして、ZoomLevelクラスを持つことは理にかなっていますか、または単にdoubleを使用する必要がありますか?

typedefすることもできますがdouble、doubleには存在しない関連する機能が必要になるまで(たとえば、ZoomLevel::default値を設定するにはクラスを定義する必要がありますが、それ、それを二重にしてください)。

すべてにクラスを使用する背後にある私の直感は、これらの仮定に基づいています[簡潔にするために省略]

これらは強力な議論です(インターフェイスを正しく使いやすく、誤って使いにくいように常に努力する必要があります)。

しかし、私が見たほとんどのC ++コードは多くのプリミティブ型を使用しており、そのための十分な理由があるに違いないと思います。

理由があります。ただし、多くの場合、これらの理由はよくありません。これを行う正当な理由の1つはパフォーマンスかもしれませんが、それはこの種のコードを規則としてではなく例外として見るべきであることを意味します。

オブジェクトを何かに使用するのは良い考えですか、それとも私が知らない欠点がありますか?

一般的に、値が常に一緒に表示される(そして意味をなさない)場合は、それらをグループ化する必要があります(投稿で言及したのと同じ理由で)。

つまり、座標が常にx、y、およびzである場合、coordinate3つのパラメーターをどこにでも送信するよりもクラスを使用する方がはるかに優れています。

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