Goの好きなとこと嫌いなとこ
asRagi.icon好きなとこ
静的型付けを採用しており静的解析に強い
型推論も簡潔な構文でストレスフリー
type aliasも簡潔で良い
プリミティブ型を用いたValueObjectとかもtype UserId stringくらい書いておけば良い
UserId型へのメソッドはどんどん生やしていける
三項演算子の排除など,不要な機能を安易に採用しない姿勢に共感を持てる.
その分即時関数とかは用意されていて,この辺りの気持ちも共感がある.
code:.go
// 三項演算子が(もしあったら)こんな感じ
userId := userResponse != nil ? userResponse.UserId : ""
// 即時関数を用いてこう書くと記述量はめちゃくちゃ増えるが,フローや意図は明確になり,拡張性もしっかりある.
userId := func(response UserResponse) UserId {
if response == nil {
return ""
}
return response.UserId
}(userResponse)
関数で書いた方がカバレッジも測れてえらい
ビルドツールやテストツールからコード生成,ライブラリなどがかなり公式で完結している
コンパイルが早いことをよしとしている
開発環境を選ばない
C#はWindowsでない場合にまあまあ困ることが起こる
deferもちょっとすき
chanもちょっとすき
多値返却を利用したエラーハンドリングが良い
try-catchを用いてネストが深くなったりしない
関数のシグネチャからエラーハンドリングの要不要が明らかになる
asRagi.icon嫌いなとこ
構造体を作ることを禁止できない
コンストラクタに限ってバリデーションやセットアップをしたいのに,そうでない形で構造体を作れてしまうために異常な値が入った構造体が常に存在しうる仕組みになっている
構造体を作る際にフィールドを指定しない場合,とくに何もなく初期値が設定されてしまう
かなりの実行時エラーの原因になっている
パターンマッチみたいなのを書きたいときとか,anyみたいな受け方をしなくてはならない場面がある.
一部ライブラリがanyで受けてるのもつらみ
code:.go
type UserRequest struct {
UserId UserId
Body RequestBody
}
type AgentRequest struct {
AgentId AgentId
Body AgentBody
}
type RequestInterface interface{}
// UserやAgentからのRequestをどちらもリスト的に処理していきたい
var requests []RequestInterface
requests := BatchGetRequest() // このとき,RequestInterfaceはinterface{}なので異常な型が入る場合がある
for _, req := range requests {
switch r := req.(type) { // 型Switch
case UserRequest:
// なんらかの処理
case AgentRequest:
// ......
}
}
C#なら無のinterfaceでも厳密に絞れる.
code:.cs
class UserRequest: IRequest {
public UserId UserId { get; }
public RequestBody Body { get; }
}
class AgentRequest: IRequest {
public AgentId AgentId { get; }
public AgentBody Body { get; }
}
interface IRequest {}
IRequest[] requests = BatchGetRequest(); // IRequestを実装していないクラスは絶対に紛れ込まない
unused variable がコンパイルエラーになる
基本ありがたいけどデバッグとかで一部の処理だけコメントアウトしたりするときにこれで引っかかって手数が増える
命名規則まわり
interface
C#だと接頭辞としてIObserverのようにIがつくため一目瞭然だが,Goではそういう命名規則がない(徹底されていない)ため,命名が難しい.
一応Senderみたいな-erの接尾辞が推奨されているらしい?
ローカル変数とprivate
Goではmodule名ふくめ簡潔な命名が推奨されているが,このためにmodule名と変数名と型名での衝突の回避に苦労する.
ユーザの処理を行うmoduleをuserとして,ユーザのmodelを表す型はinternalであればuserが最適だろうし,そのオブジェクトを格納する変数はuserとしたい.
moduleの単位やアクセスレベルのベストプラクティスが不明(またはasRagi.iconの直観に反する)
coreみたいなmoduleでUserに対する計算処理の関数を生やしたとき,それがUser以外のcore内から常に見えるのはアクセスレベルが大きくない?
じゃあUserPasswordMessageみたいにそれぞれの(C#でいうクラスの単位で)moduleを生やすのかというとそうではない
配列に対する操作をスマートに書く手段が提供されていない
.NETでいうLINQにあたるものがない
泥臭く配列の変換処理とかを書く必要がある.
メソッドに対するGo Genericsもサポートされていないため,配列の代替となる構造体を用意するのも不自然な形を取らざるを得ない場面がある.
特にBatch GETのような処理を書く際に,リクエストの配列からIDだけを抜き出すみたいな処理を書く機会は多い.
リクエストの構造体にIDを返すinterfaceを用意するというのもあるが,IDの型の数だけinterfaceと実装が生える.
#Go