web-dev-qa-db-ja.com

ロブ・パイクが「行くことは作曲についてである」と言うとき、彼は正確にはどういう意味ですか?

から 少ないほど指数関数的に多い

C++とJavaが型階層と型の分類についてである場合、Goは構成についてです。

12
CMinus

彼はあなたが次の順序で何かを使用することを意味します:

class A : public B {};

JavaまたはC++のようなもので、Goでは次のように使用します):

class A {
    B b;
};

はい、これは継承のような機能を提供します。上記の例を少し拡張してみましょう:

struct B {
    int foo() {}
};

struct A { 
    B b;
};

A a;

a.foo();  // not allowed in C++ or Java, but allowed in Go.

ただし、これを行うには、C++またはJavaで許可されていない構文を使用します-埋め込みオブジェクトを独自の名前なしで残すため、次のようになります。

struct A {
   B;
};
13
Jerry Coffin

この質問/問題は これ に似ています。

Goでは、実際にはOOPはありません。

オブジェクトを「特化」したい場合は、組み込みである埋め込みによって行いますが、継承と似た部分的な点がいくつかあります。あなたはこのようにします:

type ConnexionMysql struct {
    *sql.DB
}

このサンプルでは、​​ConnexionMysqlは* sql.DBの一種の特殊化であり、* sql.DBで定義された関数をConnexionMysqlで呼び出すことができます。

type BaseMysql struct {
    user     string
    password string
    database string
}

func (store *BaseMysql) DB() (ConnexionMysql, error) {
    db, err := sql.Open("mymysql", store.database+"/"+store.user+"/"+store.password)
    return ConnexionMysql{db}, err
}

func (con ConnexionMysql) EtatBraldun(idBraldun uint) (*EtatBraldun, error) {
    row := con.QueryRow("select pv, pvmax, pa, tour, dla, faim from compte where id=?", idBraldun)
    // stuff
    return nil, err
}

// somewhere else:
con, err := ms.bd.DB()
defer con.Close()
// ...
somethings, err = con.EtatBraldun(id)

したがって、一見すると、この構成が通常の分類法を作成するためのツールであると考えるかもしれません。

しかし

* sql.DBで定義された関数が* sql.DBで定義された他の関数を呼び出す場合、たとえ存在していても、ConnexionMysqlで再定義された関数は呼び出されません。

古典的な継承では、次のようなことがよくあります。

func (db *sql.DB) doComplexThing() {
   db.doSimpleThing()
   db.doAnotherSimpleThing()
}

func (db *sql.DB) doSimpleThing() {
   // standard implementation, that we expect to override
}

つまり、特殊化の呼び出しに関する組織としてスーパークラスでdoComplexThingを定義します。

しかしGoでは、これは特殊化された関数ではなく「スーパークラス」関数を呼び出します。

したがって、* sql.DBで定義されていてConnexionMySQL(または他の特殊化)で再定義されたいくつかの関数を呼び出す必要があるアルゴリズムが必要な場合、このアルゴリズムを* sql.DBの関数として定義することはできませんが、他の場所で定義する必要がありますこの関数は、提供された特殊化への呼び出しのみを構成します。

あなたはそれをインターフェイスを使ってこのようにすることができます:

type interface SimpleThingDoer {
   doSimpleThing()
   doAnotherSimpleThing()
}

func doComplexThing(db SimpleThingDoer) {
   db.doSimpleThing()
   db.doAnotherSimpleThing()
}

func (db *sql.DB) doSimpleThing() {
   // standard implementation, that we expect to override
}

func (db ConnexionMySQL) doSimpleThing() {
   // other implemenation
}

これは、クラス階層の従来のオーバーライドとはかなり異なります。

特に、2番目のレベルから関数の実装を継承する3番目のレベルを直接持つことはできません。

実際には、ほとんどの(直交)インターフェースの使用を終了し、実装の「スーパークラス」がそれらの呼び出しを編成するのではなく、関数に提供された実装の呼び出しを作成させます。

私の経験では、これにより、1つのレベルより深い階層が実質的に存在しなくなります。

他の言語では、概念Aが概念Bの特殊化であることがわかったときに、クラスBとクラスAをBのサブクラスとして作成することによってこの事実を具体化するために、反射を持っていることがよくあります。データを中心にプログラムすると、コード内のオブジェクトの分類法を再現することに時間を費やします。

Goでは、一般的なアルゴリズムを定義してそれを特殊化することはできません。一般的なアルゴリズムを定義し、それが一般的であり、提供されたインターフェース実装で機能することを確認する必要があります。

ロジックが最終的にすべてのレベルを意味するアルゴリズムに対応するためにコーダーが複雑なハックを行っている階層ツリーの複雑さが増していることに恐怖を感じていたため、単純なGoロジックは、たとえそれが強制されたとしても、満足していると言えるでしょう。アプリケーションモデルの概念を具体化するだけでなく、考えることです。

8
Denys Séguret