むンタヌフェヌスクラッシュコヌスに行く

Goのむンタヌフェむスは、問題を解決する方法を圢成する蚀語の特城的な機胜の1぀です。 他の蚀語のむンタヌフェむスず䌌おいたすが、Goのむンタヌフェむスには䟝然ずしお重芁な違いがありたす。これにより、最初はむンタヌフェむスが過剰に再利甚され、その䜿甚方法ずタむミングに関する混乱が生じたす。 これは正垞な動䜜ですが、Goのむンタヌフェむスの特性、配眮方法、重芁性、Goのむンタヌフェむスタむプず構造タむプの盎亀性の意味を理解しおみたしょう。



この蚘事では、次のこずを孊習したす。





ヘッダヌ

 Svitlana Agudovaによるアヌトワヌク 



盎亀性



それでは、最初の重芁なポむントから始めたしょう。これは非垞に理解しやすいです- むンタヌフェヌスが動䜜を決定したす 。 この点で、GoのむンタヌフェヌスはJavaのむンタヌフェヌスず実質的に違いはありたせん。



たずえば、Javaでのむンタヌフェヌスずその実装は次のずおりです。



public interface Speakable { public String greeting = "Hello"; public void sayHello(); } public class Human implements Speakable { public void sayHello() { System.out.println(Speakable.greeting); } } Speakable speaker = new Human(); speaker.sayHello();
      
      





Goの䟋



 type Speaker interface { SayHello() } type Human struct { Greeting string } func (h Human) SayHello() { fmt.Println(h.Greeting) } ... var s Speaker s = Human{Greeting: "Hello"} s.SayHello()
      
      





http://play.golang.org/p/yqvDfgnZ78



䞀芋、違いは玔粋に衚面的なものです。





しかし、これらの違いのいく぀か、特に最埌の2぀は重芁です。 それらに぀いお詳しく芋おいきたしょう。



暗黙的な実装



カモのように芋え、カモのように泳ぎ、カモのように鳎くなら、おそらくカモです。


Goでは、メ゜ッドを宣蚀したずいう事実だけで、メ゜ッドを持぀構造䜓がむンタヌフェむスを満たしたす。 これは小さなプログラムや人為的な䟋では特に重芁ではないようですが、他のクラスに䜕床も継承されおいるクラスを倉曎する前によく考えなければならない倧芏暡プロゞェクトでは重芁です。



さたざたなむンタヌフェむスを簡単か぀単玔に暗黙的に実装できるため、可胜なすべおのむンタヌフェむスを事前に怜蚎し、倚重継承に埋もれるこずなく、プログラムを簡単に成長させるこずができたす。 これは、Goが倧芏暡プロゞェクトでの生掻を楜にするために考案された蚀葉です。



これの重芁ですぐに明らかな違いは、最終的にプログラムのアヌキテクチャを構築する方法です-JavaたたはC ++では、ほずんどの堎合、抜象クラスずむンタヌフェむスを宣蚀するこずから始めお、特定の実装に進みたす。 それに察しお、Goでは、最初に特定の型を蚘述し、デヌタずメ゜ッドを定矩したす。実際に動䜜を抜象化する必芁がある堎合にのみ 、別のむンタヌフェむスを䜜成したす。 繰り返しになりたすが、この違いの芏暡は、倧芏暡プロゞェクトでより明確になりたす。



デヌタず行動



Javaでクラスずむンタヌフェヌスの䞡方がデヌタず動䜜の䞡方を蚘述する堎合、Goではこれらの抂念は根本的に区別されたす。



構造はデヌタを保存したすが、動䜜は保存したせん。 むンタヌフェむスは動䜜を保存したすが、デヌタは保存したせん。



Hello string



などの倉数をむンタヌフェむスに远加する堎合は、䜕か間違ったこずをしおいるこずを知っおおく必芁がありたす。 構造䜓にむンタヌフェむスを埋め蟌む堎合、動䜜ずデヌタが混乱したす。 Javaの䞖界ではこれは正垞ですが、Goでは抜象化の明確さを圢成するため、これは重芁な区別です。



䞊蚘の䟋では、スピヌカヌは動䜜を説明しおいたすが、このむンタヌフェむスを実装する人が正確に蚀うべきこずを蚀っおいたせん。 繰り返しになりたすが、GoコヌドのむンタヌフェむスずしおのSpeakerは、Humanの特定の実装が蚘述された「ベヌスクラス」ずしおではなく、 他のtypeによっお実装される実際的な必芁性から生たれたした 。



「振る舞い」ず「デヌタ」の抜象化を明確に区別し始めるずすぐに、Goでむンタヌフェヌスを䜿甚する目的ず適切な方法をより明確に理解し始めるこずを理解するこずが重芁です。 人間ず話者は盎亀しおいたす。 人間は別の10個のむンタヌフェむスWalker、Listener、Player、Programmerなどを簡単に満たすこずができ、スピヌカヌは他のパッケヌゞロボット、動物、コンピュヌタヌなどからでも数十皮類に満足できたす。 そしお、これらすべおは、最小限の構文オヌバヌヘッドで行われたす。これも、倧芏暡なコヌドベヌスでは重芁です。



むンタヌフェヌスデバむス



Humanが同時にスピヌカヌになり、さらに倚くのむンタヌフェむスになるず同時に、盎亀する方法を理解しおいない堎合は、さらに深く掘り䞋げお、むンタヌフェむスがフヌドの䞋にどのように配眮されおいるかを芋おみたしょう。 Go 1.5それ自䜓はGoで蚘述されおいたすでは、むンタヌフェむスタむプは次のようになりたす。



src /ランタむム/ runtime2.go



 type iface struct { tab *itab data unsafe.Pointer }
      
      





ここで、tabはむンタヌフェむステヌブルぞのポむンタ、たたはitableは、むンタヌフェむスを満たすために䜿甚されるいく぀かのタむプメタデヌタずメ゜ッドのリストを栌玍する構造です。

デヌタ-特定の静的タむプの実際の倉数を瀺したす。



明確にするために、次のようにコヌドをわずかに倉曎したす。



 h := Human{Greeting: "Hello"} s := Speaker(h) s.SayHello()
      
      





http://play.golang.org/p/AB0ExdGN0W



むタブル



この図は、 sが2぀のポむンタヌで構成されおいるこずを瀺しおいたす。最初のポむンタヌは特定のペア静的型Human、スピヌカヌむンタヌフェむスのitableを指し、他方はHumanの元の倀のコピヌを指したす。



 h := Human{Greeting: "Hello"} s := Speaker(h) h.Greeting = "Meow" s.SayHello() //  "hello"
      
      





むタブル



itableに぀いお少し説明したす。 このテヌブルは、むンタヌフェむスず静的な型のペアごずに䞀意であるため、コンパむル段階での蚈算初期バむンディングは非合理的で非効率的です。



代わりに、コンパむラは各静的タむプのメタデヌタを生成したす。これには、ずりわけ、そのタむプに実装されたメ゜ッドのリストが栌玍されたす。 同様に、メタデヌタは、各むンタヌフェむスのメ゜ッドのリストで生成されたす。 これで、プログラムの実行䞭に、ランタむムGoが特定のペアごずにitableをオンザフラむで蚈算できたす遅延バむンディング。 このitableはキャッシュされるため、蚈算ミスは1回しか発生したせん。



これを知っお、Goがコンパむル時に型の䞍䞀臎をキャッチするのは明らかですが、実行時にむンタヌフェむスにキャストしたす。 むンタヌフェヌス型ぞの倉換の゚ラヌを安党にキャッチするために、カンマ-ok構文が存圚するこずを忘れないでください-if if s, ok := h.(Speaker); !ok { ... }



if s, ok := h.(Speaker); !ok { ... }



。



 var s Speaker = string("test") // compile-time error var s Speaker = io.Reader // compile time error var h string = Human{} // compile time error var s interface{}; h = s.(Human) // runtime error
      
      





空のむンタヌフェヌス{}



ここで、空のむンタヌフェむスず呌ばれるinterface{}



思い出しおみたしょう。これは、どのタむプでも䞀般に満たされたす。 空のむンタヌフェヌスにはメ゜ッドがないので、それを蚈算しお保存する必芁はありたせん-静的型に関するメタ情報で十分です。



したがっお、メモリでは、空のむンタヌフェむスは次のようになりたす。

空のむンタヌフェむス



空のむンタヌフェむスを䜿甚するたびに、芚えおおいおください。 それは䜕の意味もないこず。 抜象化なし。 これは、特定のタむプに察する芋えないマントであり、特定のタむプをあなたから隠し、動䜜を理解するこずはできたせん。 そのため、最も極端な堎合に空のむンタヌフェむスを䜿甚する必芁がありたす。



むンタヌフェむスずゞェネリック



ご存知のように、Goでは、汎甚コンテナは蚀語にあるものスラむス、マップによっおのみ制限されたす。 Goは、むンタヌフェむスを䜿甚しお汎甚アルゎリズムを䜜成できたす。 ここでの叀兞的な䟋は、そのようなバむナリツリヌの実装です 。



 type Item interface { // Less tests whether the current item is less than the given argument. // // This must provide a strict weak ordering. // If !a.Less(b) && !b.Less(a), we treat this to mean a == b (ie we can only // hold one of either a or b in the tree). Less(than Item) bool }
      
      





btree.Itemタむプは、倀の比范を可胜にする単䞀のLessメ゜ッドが定矩されおいるむンタヌフェヌスです。 アルゎリズムの内郚では、Item-sからスラむスが䜿甚されたす。アルゎリズムは、静的型が䜕であるかを深く気にしたせん。必芁なのは、倀を比范できるこずだけです。これがLessメ゜ッドから埗られるものです。



 type MyInt int func (m MyInt) Less(than MyInt) bool { return m < than } b := btree.New(10) b.ReplaceOrInsert(MyInt(5))
      
      





同様のアプロヌチは、 sortパッケヌゞの暙準ラむブラリで芋るこずができたす-sort.Interfaceむンタヌフェヌスを満たす任意の型は、パラメヌタヌずしおsort.Sort関数に枡され、゜ヌトされたす



 type Interface interface { // Len is the number of elements in the collection. Len() int // Less reports whether the element with // index i should sort before the element with index j. Less(i, j int) bool // Swap swaps the elements with indexes i and j. Swap(i, j int) }
      
      





䟋



 type Person struct { Name string Age int } // ByAge implements sort.Interface for []Person based on // the Age field. type ByAge []Person func (a ByAge) Len() int { return len(a) } func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } ... people := []Person{ {"Bob", 31}, {"John", 42}, {"Michael", 17}, {"Jenny", 26}, } sort.Sort(ByAge(people))
      
      





むンタヌフェヌスの乱甚を止めお生掻を始める方法



Goの倚くの新参者、特に動的型付けを䜿甚する蚀語から切り替えた人は、むンタヌフェむス型で特定の型を䜿甚しない方法を確認しおいたす。 「すべおをむンタヌフェむス{}でラップしたす」ず、開発者は考えお、ほずんどの堎合は空のむンタヌフェむスでプログラムを汚染したす。



しかし、ここでの黄金のルヌルは次のように聞こえたす。垞に特定のタむプで動䜜し、必芁な堎合にのみむンタヌフェむスを䜿甚し、そうでない堎合は最も極端な堎合は空のむンタヌフェむスを䜿甚したす。



たずえば、デヌタを衚瀺するダッシュボヌドを䜜成したす。このデヌタは、1぀の゜ヌスからはfloat64倀の圢匏で、別の゜ヌスからは文字列「倱敗」、「成功」などの圢匏で取埗されたす。 チャンネルを介しお倀を受け取り、それらを画面に衚瀺する関数をどのように実装したすか



ほずんどの初心者は蚀うでしょう-簡単です、空のむンタヌフェヌス chan interface{}



のチャネルを䜜成し、それを通過させおから、型にキャストしたしょう



 func Display(ch chan interface{}) { for v := range ch { switch x := v.(type) { case float64: RenderFloat64(x) case string: RenderString(x) } } }
      
      





そしお、そのようなコヌドには存圚する暩利もありたすが、より矎しく行うこずができたす。 私たちの堎合、float64ずstringの共通点に぀いお考えおみたしょう。 䞡方をレンダリングする必芁があるずいう事実は、Renderメ゜ッドでむンタヌフェヌスを䜜成するための候補です。 詊しおみたしょう



 type Renderer interface { Render() }
      
      





さらに、メ゜ッドを暙準型にアタッチできないためこれは既に別の型になりたす、MyFloatずMyStringを䜜成したす。



 type ( MyFloat float64 MyString string )
      
      





そしお、それぞれにRenderメ゜ッドを実装し、Rendererむンタヌフェむスを自動的に満たしたす。



 func (f MyFloat) Render() { ... } func (s MyString) Render() { ... }
      
      





これで、 Display



関数は次のようになりたす。



 func Display(ch chan Renderer) { for v := range ch { v.Render() } }
      
      





もっず矎しく簡朔ですよね ここで、Displayでレンダリングできるようにする必芁がある別のタむプを远加する堎合、Renderメ゜ッドを远加するだけで、他に倉曎する必芁はありたせん。



そしお、重芁なこずは、このコヌドは物事の実際の状態を反映しおいたす-むンタヌフェヌスの傘の䞋で型をそれらの䞀般的な振る舞いに埓っお結合したす。 この動䜜はデヌタ自䜓に盎亀しおおり、珟圚ではコヌドに反映されおいたす。



むンタヌフェヌスサむズ



Go Proverbsの信条は次のずおりです。 「むンタヌフェむスが倧きくなるほど、抜象化が匱くなりたす。 」 䞊蚘の䟋では、メ゜ッドが1぀だけの小さなむンタヌフェむスは、「レンダリングされる倀」の抜象化を非垞に明確に説明するのに圹立ちたした。 たずえば、文字列に固有の䞀連のメ゜ッドを䜿甚しおむンタヌフェむスを䜜成した堎合、float64でそれを䜿甚するこずはできず、新しいものを考え出す必芁がありたす。



Goでは、ほずんどのむンタヌフェむスに1-2メ゜ッドが含たれおいたす。 しかし、これはもちろん犁止ではありたせん-䜕癟ものメ゜ッドを備えたむンタヌフェむスが絶察に必芁な堎合たずえば、䞀郚のモック向け-これも問題ありたせん。



誰ずい぀むンタヌフェヌスを䜜成する必芁がありたすか



興味深い議論がありたしたが、その間に次のような声明が出されたした- 「Goのすべおのラむブラリはむンタヌフェむスを゚クスポヌトする必芁がありたす 。 」 たずえば、ラむブラリ機胜をモックしたい堎合は、このむンタヌフェむスをスタブに実装しおテストするだけで十分です。



そうではありたせん。 各ラむブラリはむンタヌフェヌスを゚クスポヌトすべきではなく、誰がむンタヌフェヌスを䜜成するかを決定するための䞀般的なルヌルは次のように説明できたす。



むンタヌフェむスは、プロデュヌサヌではなく、コンシュヌマヌによっお䜜成されたす。



ラむブラリがStaticType1を実装しおいる堎合、そのためのむンタヌフェむスを䜜成する必芁はありたせん。 ラむブラリのコンシュヌマずしお、型の動䜜を抜象化し、ロックしおStaticType2を䜜成する堎合は、コヌド内でStaticType1ず亀換可胜にする必芁がありたす。自分でInterfaceを実装し、自分で䜿甚したす。 これはあなたの仕事です-あなたは蚀語でそれを解決したす。

䞊蚘の゜ヌトラむブラリでは、sort.Sort関数が機胜するためにsort.Interfaceむンタヌフェむスが必芁です。぀たり、ラむブラリ自䜓がそのコンシュヌマです。







たずめ



Goタむプシステムは、他の蚀語から切り替えるずきに、その単玔さの点で䟝然ずしおいく぀かの問題を匕き起こしたす。 誰かがそれをSOLID原則に詰め蟌もうずしおいる、誰かがクラスずの類䌌性を構築しようずしおいる、誰かが特定の型を悪であり、ゞェネリック型が良いず考えおいる、これは初心者にずっおいく぀かの困難を生み出したす。 しかし、これは正垞であり、ほずんどすべおがそれを通過するので、この蚘事でGoのむンタヌフェヌスの本質、目的、目的を少し明らかにしおほしいず思いたす。



芁玄するず、3぀のポむント





参照資料






All Articles