2009-11-13 13 views

答えて

29

コメントでは、埋め込みのアイデアが「継承を完全に置き換える」のに十分かどうか疑問に思った。その質問に対する答えは「はい」と言います。数年前、私はTcl OOシステム(Snit)を使って簡単に遊んでいました。これは、継承を除外して構成と委任を使用しました。SnitはGoのアプローチとはまだ大きく異なりますが、その点で共通の哲学的根拠があります。これは、クラスの階層ではなく、機能と責任の部分を結合するためのメカニズムです。

他の人も述べているように、実際には、言語設計者がどのようなプログラミング手法をサポートしたいのかということです。すべてのそのような選択は彼ら自身の長所と短所を伴います。私は「ベストプラクティス」は必ずここに当てはまるフレーズだとは思わない。私たちはおそらくGoのための継承層を最終的に開発する人がいることに気付くでしょう。

(Tclに精通している読者なら、私はSnitが[incr Tcl]であったよりも、言語の "感じ"に少し近い一致をしていると感じました。Tclは少なくとも私の考え方に委任についてです)

36

Gang of 4の重要な原則は "継承するために組成物を好む" です。 はあなたがそれに従うようにになります;-)。継承のための

+4

継承は過度に使用されています。私が本当に知りたいのは、埋め込みが継承を完全に置き換えることができるかどうかということです。私はこれが実際に行くといくつかのコードを書くことなく答えるのは難しい質問だと思います。 – Casebash

+0

まあ、Template Methodのようなキー継承ヒンジ付きデザインパターンを(直接)取得しないのですが、それはキラーではないようです非常に最悪のケースでは、ある程度利便性が失われているように見えます(明示的なコーディングが少し必要です)。 –

+0

@Casebash:私たちが言うことができるJSプロトタイプは、人々が作っただけのものです。 –

12

唯一の本当の用途は以下のとおりです。

  • ポリモーフィズム

    • ゴーのインターフェースの「静的ダックタイピング」システムは、この問題を解決
  • 別のクラス

    からの借入実装
    • これは埋め込みが何のためにあるのかである

正確に1対1マッピングされていないゴーのアプローチは、Javaでの継承やポリモーフィズムのこの古典的な例(based on this)考えてみます。

//roughly in Java (omitting lots of irrelevant details) 
//WARNING: don't use at all, not even as a test 

abstract class BankAccount 
{ 
    int balance; //in cents 
    void Deposit(int money) 
    { 
     balance += money; 
    } 

    void withdraw(int money) 
    { 
     if(money > maxAllowedWithdrawl()) 
      throw new NotEnoughMoneyException(); 
     balance -= money; 
    } 

    abstract int maxAllowedWithdrawl(); 
} 

class Account extends BankAccount 
{ 
    int maxAllowedWithdrawl() 
    { 
     return balance; 
    } 
} 

class OverdraftAccount extends BankAccount 
{ 
    int overdraft; //amount of negative money allowed 

    int maxAllowedWithdrawl() 
    { 
     return balance + overdraft; 
    } 
} 

ここでは、継承と多型が組み合わされており、基礎となる構造を変更することなくこれをGoに変換することはできません。

私は行くに深く掘り下げていないが、私はそれがこのようなものになりますと仮定します。音符当たりとして

//roughly Go? .... no? 
//for illustrative purposes only; not likely to compile 
// 
//WARNING: This is totally wrong; it's programming Java in Go 

type Account interface { 
    AddToBalance(int) 
    MaxWithdraw() int 
} 

func Deposit(account Account, amount int) { 
    account.AddToBalance(amount) 
} 

func Withdraw(account Account, amount int) error { 
    if account.MaxWithdraw() < amount { 
     return errors.New("Overdraft!") 
    } 
    account.AddToBalance(-amount) 
    return nil 
} 

type BankAccount { 
    balance int 
} 

func (account *BankAccount) AddToBalance(amount int) { 
    account.balance += amount; 
} 

type RegularAccount { 
    *BankAccount 
} 

func (account *RegularAccount) MaxWithdraw() int { 
    return account.balance //assuming it's allowed 
} 

type OverdraftAccount { 
    *BankAccount 
    overdraft int 
} 

func (account *OverdraftAccount) MaxWithdraw() int { 
    return account.balance + account.overdraft 
} 

を1が行くでJavaをやっているから、これは完全にコーディングする間違った方法であります。 Goにこのようなことを書いていたら、おそらくこれとはかなり異なる組織になるでしょう。

+0

あなたはこれがコンパイルされないと言っていますが、これを読んでいる人を助けるためのいくつかのポイントがあります: タイプはGoでタイプリテラルが必要です。 'type RegularAccount {} 'ではなく' type RegularAccount struct {} 'を使用してください funcプロトタイプを型定義に入れることはできません。 'func(this * receiverType)funcName(parms)returnType' 値を返す関数の戻り値の型を指定する必要があります。 'func(account * RegularAccount)maxWithdraw()int {}' 最後に、 "func"行を開始ブレースで終わらせる必要があります。 – burfl

+0

私はこれを練習問題として書いてみました - 私はGoの早い日です...私はほとんど仕事に慣れました。 https://gist.github.com/mindplay-dk/807179beda57e676b8fb –

3

私は今Goについて学んでいますが、あなたは意見を求めているので、私がこれまで知っていることに基づいて提示します。埋め込みは、既存の言語で既に実行されているベストプラクティスの明示的な言語サポートであるGoの他の多くの機能によく見られるようです。例えば、Alex Martelliが指摘したように、4人のギャングは「継承への構成を好む」と言っています。 Goは継承を削除するだけでなく、C++/Java/C#よりもより簡単でより強力なコンポジションを作成します。

「Goは私が言語Xでやっていないことは何も新しいものを提供しません」「なぜ他の言語が必要なのですか?」というようなコメントに困惑しました。ある意味では、Goは何らかの仕事で前に行うことができなかった新しいことを何も提供していないように見えますが、別の意味では、Goは最高のテクニックの使用を促進し、既に実際には他の言語を使用しています。

+3

いくつかの点で、Goの新機能は、取り除かれたことです。これが新しい言語の重要な理由です。機能を追加するだけの場合は、C+++;)でも機能を継承するには(継承、ポインタ算術、手動メモリ割り当て)、新しい言語が必要です。 –

3

フォークは、Goに埋め込みに関する情報へのリンクをリクエストしています。

ここでは、埋め込みについて説明し、具体的な例を示した「効果的な」ドキュメントを示します。

http://golang.org/doc/effective_go.html#embedding

それはメソッドのセットの名前としてのインターフェースを考えて、あなたが考える場合の例では、すでに移動インタフェースと種類を十分に把握を持っているより多くの意味を成していますが、偽物できますC構造体に似た構造体。

構造体の詳細については

は、明示的に組み込み型として構造体の無名のメンバーに言及しているのGo言語の仕様を、見ることができます:

http://golang.org/ref/spec#Struct_types

は、これまでのところ、私は唯一の便利な方法としてそれを使用しましたフィールド名がソースコードに値を追加しないときに、内部構造体のフィールド名を使用することなく、ある構造体を別の構造体に入れること。以下のプログラミング演習では、プロポーザルと応答チャネルを持つ型の中にプロポーザル型をバンドルしています。

https://github.com/ecashin/go-getting/blob/master/bpaxos.go#L30

7

埋め込み自動委任を提供します。埋め込みは多態性の形を提供しないので、これ自体は継承を置き換えるには十分ではありません。 Goインタフェースは多型性を提供しますが、使用する可能性のあるインタフェースとは少し異なります(ダックタイピングや構造型の入力に類似している人もいます)。

他の言語では、変更が幅広く行われるため、継承階層を慎重に設計する必要があります。 Goはこれらの落とし穴を避け、強力な代替手段を提供します。

はここでもう少し行くとOOP掘り下げ記事です:http://nathany.com/good

3

私はそれが好きです。

使用する言語は、あなたの思考パターンに影響します。 (Cのプログラマーに "単語数"を実装するよう依頼するだけで、おそらくリンクされたリストを使用して、パフォーマンスのためにバイナリツリーに切り替えることになるでしょうが、すべてのJava/Ruby/PythonプログラマはDictionary/Hashを使用します。他のデータ構造を使用することはできません)。

継承を使用すると、抽象的なものから構築し、次に詳細にサブクラス化する必要があります。あなたの実際の便利なコードはNレベルの深さのクラスに埋め込まれます。これは、親クラスでドラッグしないでコードを再利用することができないため、オブジェクトの「一部」を使用するのを難しくします。

Goでは、インターフェイスを使用してこのようにクラスをモデル化できます。しかし、あなたはこのようにコードすることはできません。

代わりに、埋め込みを使用できます。あなたのコードは、それぞれ独自のデータを持つ小さな独立したモジュールに分割できます。これにより、再利用が簡単になります。このモジュール性は、あなたの「大きな」オブジェクトとほとんど関係がありません。 (すなわち、In Goでは、あなたのDuckクラスについて知らない "quack()"メソッドを書くことができます。しかし、典型的なOOP言語では、 "私のDuck.quack()実装は宣言できません。他のDuckのメソッド」を参照してください)。

Goでは、プログラマーはモジュール化について常に考えなければなりません。これは、カップリングの低いプログラムにつながります。カップリングが低いとメンテナンスがずっと簡単になります。 (「ああ、見て、Duck.quack()は本当に長く複雑ですが、少なくともDuckの残りの部分に依存しないことはわかっています」)。

関連する問題