Golangのsc責とその対処方法

Goでいくつかのプロジェクトを書いた後、振り返りました。 私はその言語の長所と短所を見ました。 この記事では、Goが批判されていることについてお話したいと思います。 もちろん、OOP自体の欠如、メソッドと関数のオーバーロード、一般化されたプログラミングと例外についてお話します。 それは本当に多くの問題を引き起こしますか? それとも開発アプローチの問題ですか? これらの問題を解決した経験を共有します。



発行



JavaとPHPの後、Goでプログラミングを開始しました。 そして今、その理由をお話しします。



Javaはクールなものです。 いい構文です。 多くの大規模なプロジェクトが戦いでそれを使用しています。 JVMでなければ、すべてがクールになります。 本格的なJavaアプリケーションをデプロイするには、大量のRAMを搭載した車が必要です。 そして、これはスタートアップにとって完全に不適切です(私の場合)。



同様に、PHPでは、リクエストごとにアプリケーションが新たに初期化され、すぐに使用できるマルチスレッドはなく、パフォーマンスが低下します。



各プログラミング言語、および各テクノロジーには、独自の弱点があります。 パフォーマンス、システムリソース、開発の複雑さなど、常に何かを犠牲にする必要があります。



この点で行くことも例外ではありません。 優れたパフォーマンスを提供し、経済的にリソースを消費するには、開発者による特別なアプローチが必要です。 JavaのようにGoでアプリケーションの開発に取り組むと、本当に困難が生じます。 OOP、メソッドと関数のオーバーロード、一般化されたプログラミング、例外などの身近なものの欠如に直面しています。



使い慣れたソリューションがない場合、それらは新しいものに置き換えられますが、それほど効果的ではありません:柔軟な型システム、インターフェイス、データと動作の分離。 次に、すべてを例で示します。



メソッドと関数のオーバーロード



Goにはメソッドと関数のオーバーロードはありません。 メソッドと関数には異なる名前を付けることをお勧めします。



func SearchInts(a []int, x int) bool func SearchStrings(a []string, x string) bool
      
      





別のアプローチは、インターフェースを使用することです。 例として、検索用のインターフェイスと関数を作成します。



 type Slice interface { Len() int Get(int) interface{} } Search(slice Slice, x interface{}) bool
      
      





これで、2つのタイプを作成するだけで十分です。



 type Ints []int type Strings []string
      
      





そして、各タイプにインターフェースを実装します。 その後、文字列と数字の両方の検索を使用できます。



 var strings Strings = []string{"one", "two", "three"} fmt.Println(Search(strings, "one")) // true fmt.Println(Search(strings, "four")) // false var ints Ints = []int{0, 1, 2, 3, 4, 5} fmt.Println(Search(ints, 0)) // true fmt.Println(Search(ints, 10)) // false
      
      





OOP



Goには、私たちが慣れているOOPがありません。 GoのOOPは基本的に、親メソッドを子メソッドでオーバーロードする機能を備えた型を埋め込みます。 例:



 //  type A struct {} //      func (a *A) CallFirst() { fmt.Println("A CallFirst") } //  type B struct { A } //     func (b *B) CallFirst() { fmt.Println("B CallFirst") } a := new(A) a.CallFirst() // "A CallFirst" b := new(B) b.CallFirst() // "B CallFirst"
      
      





この場合、すべてが必要に応じて機能します。 子孫でオーバーライドされたメソッドに操作が依存する親タイプのメソッドの実装が必要な場合はどうすればよいですか? 複雑なロジックを持つメソッドと、オーバーライドするためのいくつかのメソッドを親タイプに追加します。



 //     func (a *A) CallSecond() { fmt.Println(a.GetName(), a.GetMessage()) } //      func (a *A) GetName() string { return "A" } //      func (a *A) GetMessage() string { return "CallSecond" } //     func (b *B) GetName() string { return "B" } a.CallSecond() // “A CallSecond” b.CallSecond() // “A CallSecond”,   “B CallSecond”
      
      





私は自分でこのソリューションを選択しました-親と子のインターフェースを作成して実装します。 複雑なメソッドを呼び出す場合、親と子の両方のインターフェイスにリンクを渡します。



 //   type SuperType interface { GetName() string GetMessage() string CallSecond() } //     func (a *A) allSecond(s SuperType) { fmt.Println(s.GetName(), s.GetMessage()) } //      func (a *A) CallSecond() { a.callSecond(a) } //     func (b *B) CallSecond() { b.callSecond(b) } a.CallSecond() // “A CallSecond” b.CallSecond() // “B CallSecond”
      
      





エレガントではないかもしれませんが、ロジックの重複は避けます。 さらに、メソッドまたは関数が引数としてジェネリック型を取る場合、インターフェイスが必要になります。



 //     type C struct { A } func (c *C) GetName() string { return "C" } func (c *C) CallSecond() { c.callSecond(c) } // ,     A, B  C func DoSomething(a *A) { a.CallSecond() } DoSomething(a) DoSomething(b) // ,    DoSomething(c) // ,   
      
      





インターフェイスを受け入れるようにDoSomething関数を作り直しましょう:



 // ,     A, B  C func DoSomething(s SuperType) { s.CallSecond() } DoSomething(a) // “A CallSecond” DoSomething(b) // “B CallSecond” DoSomething(c) // “C CallSecond”
      
      





このようにして、データを動作から分離します。これは良い習慣です。



一般的なプログラミング



それでも、Goには一般化されたプログラミングがあり、これはインターフェイス{}です。 繰り返しますが、これは異常です。 Javaのような構文上の砂糖はありません。



 ArrayList<String> list = new ArrayList<>(); String str = list.get(0);
      
      





Goでは何が得られますか?



 type List []interface{} list := List{"one", "two", "three"} str := list[0].(string)
      
      





私の意見では、違いは大きくありません! インターフェイスを使用すると、明示的な型変換を回避できます。 例を挙げます。



 //   type Getter interface { GetId() int } type Setter interface { SetId(int) } //   type Identifier interface { Getter Setter } //    type List []Identifier
      
      





識別子を実装するいくつかのタイプと、インターフェースで機能する関数を追加します。



 //  Identifier type User struct { id int } //  Identifier type Post struct { id int } func assign(setter Setter, i int) func print(getter Getter)
      
      





明示的なキャストなしで配列をループできます



 list := List{new(User), new(User), new(Post), new(Post), new(Post)} for i, item := range list { assign(item, i) print(item) }
      
      





エラー処理



言語にはtry / catchブロックがありません;代わりに、メソッドと関数はエラーを返します。 誰かがこれを言語の欠如と考え、誰かはそうではありません。 基本的にtry / catchを使用しない人がいます。 これは遅いブロックです。 原則として、標準のエラー処理で十分です。



 func Print(str string) error if Print(str) == nil { //     } else { //  }
      
      





try / catchブロックのように複雑なエラー処理が必要な場合は、次の手法を使用できます。



 switch err := Print(str); err.(type) { case *MissingError: //   case *WrongError: //   default: //     }
      
      





したがって、switchコンストラクトを使用すると、try / catchの不在を最小限に抑えることができます。



まとめると



では、言語には本当に深刻な問題がありますか? 私はそうは思いません! これは習慣の問題です! 私にとって、Goは、毎秒何万ものリクエストを処理できるシステムユーティリティ、ネットワークデーモン、またはWebアプリケーションを作成する必要がある場合に最適なソリューションです。 Goは若いが、非常に有望であり、彼の問題をうまく解決している。 Goは、慣れる、慣れる、快適に感じるために必要な新しいアプローチを提供します。 私はすでにそれをしました、私もあなたに助言します!



PS:例の完全なコードはこちらから入手できます



All Articles