5

Scala や OCaml などの他の言語で構造型付けをいじってから、Go を調べ始めました。言語間でいくつかの慣用的な手法をマッピングしようとしています。次のタイプを検討してください

type CoordinatePoint struct {
    x int
    y int
    // Other methods and fields that aren't relevant
}

type CartesianPoint struct {
    x int
    y int
    // Other methods and fields that aren't relevant
}

これらの型の両方を操作して極座標表現 を計算するメソッドを書きたいとしましょうfunc ConvertXYToPolar(point XYPoint) PolarPointCartesianPointand型がandフィールドCoordinatePointの getter メソッドと setter メソッドを定義した場合、これらのメソッドとの共通インターフェイスとして定義でき、両方の型を操作できますが、現状では、インターフェイスはフィールドを宣言できず、メソッドのみを宣言できます。xyXYPoint

これに基づいて、いくつか質問があります。

  1. Goでこれを処理する慣用的な方法は何ですか?
  2. 既存のタイプを変更せずに実行できますか?
  3. 型の安全性を維持できますか。つまりConvertXYToPolar、空のインターフェイス型をパラメーターとして使用せずに定義し、手動で変換することを回避できますか?
  4. インターフェイスと暗黙的なインターフェイスの満足がGoのポリモーフィズムの主要なツールである場合、インターフェイス定義のフィールドの禁止は制限されていますか?
  5. この制限を回避するために、構造体でゲッター/セッターメソッドが一般的に定義されていますか?
  6. インターフェイス定義でフィールドをサポートしないという設計上の決定の背後に説得力のある理由はありますか?

組み込み型の単純さ、暗黙的なインターフェイスの満足度、インターフェイスベースのポリモーフィズムは、コードの再利用性と保守性を促進するための非常にシンプルで魅力的な手法の組み合わせであると思いますが、インターフェイス定義でフィールドを禁止すると、私の観点からは Go の構造型付け機能がいくらか制限されます. 簡単な解決策がありませんか?

4

3 に答える 3

11

通常の方法は、合成を使用することです:

type Point struct {
    x int
    y int
}

type CoordinatePoint struct {
    Point
    other stuff
}

type CartesianPoint struct {
    Point
    Other methods and fields that aren't relevant
}

Go 構文により、この構成はほとんど他の言語の継承のように感じられます。たとえば、これを行うことができます:

cp := CoordinatePoint{} 
cp.x = 3
log.Println(cp.x)

Pointそして、パラメータとして受け取る関数を呼び出すことができます

doAThingWithAPoint(cp.Point)

ポイントを交換可能に渡すには、インターフェイスを定義する必要があります

type Pointer interface {
    GetPoint() *Point
}
func (cp CoordinatePoint) GetPoint() *Point {
    return &cp.Point
}

次に、次の値を取る関数を定義できますPointer

func doSomethingWith(p Pointer) {
    log.Println(p.GetPoint())
}

GetXSetXGetYおよび を定義するインターフェイスに基づく別の解決策もありますSetYが、個人的には、このアプローチは必要以上に重く、冗長であると感じています。

于 2013-03-29T13:06:50.607 に答える
2

私の最初のドラフトは次のようになります。

package points

type XYPoint struct {
    X, Y int64
}

type CoordinatePoint struct {
    XYPoint
}

type CartesianPoint struct {
    XYPoint
}

type PolarPoint struct {
    R, T float64
}

type XYToPolarConverter interface {
    ConvertXYToPolar(point XYPoint) PolarPoint
}

func (cp *CoordinatePoint) ConvertXYToPolar(point XYPoint) PolarPoint {
    pp := PolarPoint{}
    // ...
    return pp
}

func (cp *CartesianPoint) ConvertXYToPolar(point XYPoint) PolarPoint {
    pp := PolarPoint{}
    // ...
    return pp
}
于 2013-03-29T13:17:55.990 に答える
1
  1. 通常、慣用的な方法はゲッターとセッターを使用することです。不便?多分。しかし、少なくとも今のところはそのようになっています。
  2. はい。それがダックタイピングの本質です。明示的に実装する必要なく、インターフェイスに一致するすべての型が受け入れられます。編集:この回答に関するコメントに従って、この質問を誤解しました。答えはノーです。これらの構造体が 以外のインターフェイスに一致するようにメソッドを追加する必要がありますinterface{}
  3. はい、ゲッターとセッターを使用します。
  4. 多分。ゲッターとセッターがあまり便利ではないと認識される理由がわかりました。しかし、私が知る限り、彼らはあなたができることを厳密に制限していません.
  5. はい。これは、他の人のコードや標準ライブラリで見た方法です。
于 2013-03-29T13:18:48.787 に答える