GolangとOOP

トピック「OOP言語を使用しますか?」に関するブログ投稿にうんざりしていない場合は、別のトピックを参照してください。 そして、短い答えは「はい、しかしそれは問題ではありません」です。

ただし、用語や学術的な定義に焦点を当てるのではなく、Goでこれを行う方法に焦点を当てます。







OOPとは何ですか?



通常、Goのオブジェクト指向の問題は、「OOP言語にはクラスが必要だ」という恐ろしく一般的な誤解に由来しますが、ご存じのとおりGoはそうではありません。 質問者や回答者は、「言語がカプセル化、多型性、継承をサポートしている場合、OOPである」などの定式化に導かれます。実際、OOPを発明したAlan Kayは、まったくの誤解を持っています。



ほとんどの読者にとって、OOP言語の標準はC ++であり、Alan Kay自身次のように述べていると思います。

「オブジェクト指向」という用語を作成しましたが、C ++を念頭に置いていなかったことがわかります。



-アラン・ケイ、OOPSLA '97


(ちなみに、ビデオは素晴らしいです。まだ見ていませんが。)



そしてすぐに、 OOPの意味について:

私にとってのOOPとは、メッセージング、ローカルの保持と状態プロセスの保護と隠蔽、およびすべてのものの極端な遅延バインディングのみを意味します。




一般に、トピックを掘り下げて、お気に入りのOOPブックが最も正しいと主張しない場合、アイデアはまだ現実の世界を知覚する経験をコード開発モデルに移すことでした。 世界にはオブジェクトがあり、オブジェクトにはプロパティと動作があり、オブジェクトは相互作用できます。 ポイント。 結局のところ、私たちは世界を理解しています。 後で登場した他のすべての定義と概念は、この単純な概念を実装するための単なる方法です。



Goは遅延バインディングが好きではありませんが、メッセージングが大好きであり、オブジェクトの「プロパティと動作」の概念は完全に実装されています。



主観的には、Goで1年半働いた後、「プロパティ」と「動作」に根本的に分けることは、エンティティを表す最も自然な方法であるように思われます。 1つのヒープ内にメソッドとプロパティを持つクラスは、今では心から不快で古いアタビズムのように思えます。



例によるOOP



例えば、あなたはスノーデンの元パートナーであり、有名な人格の個人情報を監視するシステムを書いています:)「おばあさん」というフレーズに言及したすべてのメッセージを取得する必要があります。 メッセージには、音声、スカイプ、電子メール、Twitterメッセージなどがあります。



クラス指向の言語では、ほとんどの場合、Messageクラスのオブジェクトを返す仮想LookupWord()メソッドを使用してMessageSourceクラスを作成します。 TwitterのTwitterSourceクラスとSkypeのSkypeSourceクラス。 クラスに慣れているすべての人に親しみやすく理解しやすい。



Goでは、これをより簡単な方法で表現できます。プロパティと動作を別々に記述し、Goにはない通常の継承の代わりに構成を使用します。



構造


最初にプロパティを説明します-このために、オブジェクトのプロパティに必要なフィールドを含む構造を作成します。 Goの構造は、データ型を作成する主な方法です。



type Message struct { Data []byte MimeType string Timestamp time.Time } type TwitterSource struct { Username string } type SkypeSource struct { Login string MSBackdoorKey string }
      
      





もちろん、実際の実装にはもっと面白いものが含まれますが、そのアイデアは明確です。 メッセージは、メッセージに関するデータ、TwitterSource、およびSkypeSourceを記述します-メッセージソースに関するデータ。



可視性


Goは、名前の最初の文字に大文字と小文字のルールを使用します。名前が大文字で始まる場合、これはパブリックアクセスです。小文字がプライベートの場合、パブリックアクセスです。 最初は、 言論の自由の侵害は不便に思えるかもしれませんが、Goの最初の行から、このソリューションがいかに便利であるかを理解できます。



APIを開発しているときにフィールドを非表示にしたい場合は、Usernameではなく単にusernameと呼びます。 そして、ゲッターとセッターを作成することを誰も禁止していません。 Goでは、ゲッターとセッターにそれぞれフォームX()とSetX()を使用するのが慣習です。



インターフェース


それでは、振る舞いをしましょう。 最初に頭に浮かぶのは、メッセージのソースが単語を検索できなければならないということです。 それを書き留めましょう:



 // Finder represents any source for words lookup. type Finder interface { Find(word string) ([]Message, error) }
      
      





これは、1つのメソッドを持つインターフェイスがこのメソッドの名前+ 'er'と呼ばれる場合の一般的な方法です:検索-→Finder。 Goのほとんどのインターフェイスでは、1〜2個のメソッドが記述されています。



これで、上記のシグネチャを持つFind()メソッドが定義されるデータ型はすべて、Finderインターフェイスを自動的に満たします。 アヒルのタイピング-アヒルのように泳いだり、アヒルのように見えたり、アヒルのように鳴ったりする場合、それはアヒルです。



方法


Goのメソッドは、特定のタイプに固有の関数です。 メソッドをクラスのメソッドとして表現することも、this / selfへのポインター(またはコピー)を持つ通常の関数として表現することもできます。 「this」になるオブジェクトは、funcキーワードと名前の間の括弧内に示されます。



 func (s TwitterSource) Find(word string) ([]Message, error) { return s.searchAPICall(s.Username, word) } func (s SkypeSource) Find(word string) ([]Message, error) { return s.searchSkypeServers(s.MSBackdoorKey, s.Login, word) }
      
      





thisおよびselfという名前を使用しないでください。これは他の言語からの不必要なトレーシングペーパーであり、Goではそのように記述しません。



これらのメソッドを作成することにより、実際にFinderとして使用できる2つのオブジェクトを取得しました。



変更のための少しのアニメーション:







それでは、すべてをまとめましょう。



 type Sources []Finder func (s Sources) SearchWords(word string) []Message { var messages []Message for _, source := range s { msgs, err := source.Find(word) if err != nil { fmt.Println("WARNING:", err) continue } messages = append(messages, msgs...) } return messages }
      
      





新しいタイプのソース-ファインダーの配列-を追加しました。これらはすべて、検索機能を提供します。 このタイプのSearchWords()メソッドは、メッセージを含む配列を返します。



リテラル


Goでは、リテラルを使用して宣言中に任意のオブジェクトを初期化できます。



 var ( sources = Sources{ TwitterSource{ Username: "@rickhickey", }, SkypeSource{ Login: "rich.hickey", MSBackdoorKey: "12345", }, } person = Person{ FullName: "Rick Hickey", Sources: sources, } )
      
      







埋め込み


NSAビューのIDは、名前とそのプライベート情報のソースで構成されます)Sources型の名前のないフィールドを追加して、埋め込み-「埋め込み」を使用します。不必要な操作なしで、Person型のオブジェクトがSources関数を直接使用できます:



 type Person struct { FullName string Sources } func main() { msgs := person.SearchWords("  ") fmt.Println(msgs) }
      
      





PersonのSearchWords()メソッドをオーバーライドすると、優先されますが、Sources.SearchWords()を呼び出すオプションがまだあります。



おわりに


これで、プログラムの出力は次のようになります。



 $ go run main.go WARNING: NSA cannot read your skype messages ;) [{[82 101 109 101 109 98 101 114 44 32 114 101 109 101 109 98 101 114 44 32 116 104 101 32 102 105 102 116 104 32 111 102 32 78 111 118 101 109 98 101 114 44 32 208 181 209 129 208 187 208 184 32 208 177 209 139 32 208 177 208 176 208 177 209 131 209 136 208 186 208 176 46 46 46] text/plain 2014-11-18 23:00:00 +0000 UTC}]
      
      





これは、構造の標準の文字列表現です。 標準ライブラリは、1つのメソッドString()stringを定義する単純なStringerインターフェイスを広範囲に使用します。 このメソッドを実装するオブジェクトは自動的にストリンガーになり、標準出力関数で使用されると、このメソッドによって実装される方法で表示されます。



例:



 func (m Message) String() string { return string(m.Data) + " @ " + m.Timestamp.Format(time.RFC822) }
      
      





これで、fmt.Println(msg)の出力はより快適になります。



 $ go run main.go WARNING: NSA cannot read your skype messages ;) [Remember, remember, the fifth of November,   ... @ 18 Nov 14 23:00 UTC]
      
      







コード全体play.golang.org/p/PYHwfRmDmK



結論



Goはオブジェクト指向言語ですか?

もちろん、おそらくAlan Kayの理解では、最もオブジェクト指向の1つでもあります。



しかし、そうでないとしても、Alan KayはGoのアプローチを承認しません。GoのOOデザインがあなたを喜ばせ、問題領域を開発言語に効果的に転送することを可能にする場合、どのような違いがありますか?



落ち着いて、Goで次のプログラムを書いてください。










All Articles