go言語のtype 〇〇 interface { ... } を頑張る
C#だけど一番わかりやすい
goでinterfaceをリファクタリングの手段として解説してくれる
interface
… で、これ何に使うねん?って私は最初に思いました。ここまではTour of Goを読んでいたら理解できると思いますが、実際どんな場| 面で使うのか全くわからなかったです。まだざっくりとした理解ですが、interfaceを使っていいことは具体的な実装を気にすることなく1つのまとまった塊として定義できることで、他の関数から扱いやすくなるといったところでしょうか?抽象的なことをいってもわからないので、具体例をあげます。
メリット2: 実装を隠蔽する
これまでのコードでは単一のパッケージで実装していたため、やろうと思えば特定の型に依存したコードが書けてしまいます(例えば whiteTaiyaki.Nakami = "クリーム" というような書き方)。これではコードが密結合になってしまい、単体テストが書きづらくなったり、実装の変更時に影響範囲の把握が困難になったりして後々非常に苦労することになってしまいます。
ところで、Golangではパッケージを境界として関数や構造体の公開・非公開を制御することができます(Golangの基本:頭文字が大文字であればパッケージ外へ公開、小文字であれば非公開)。これを利用しインターフェースの実装部分のみを外部公開することで中身の構造体や内部処理に使う関数を隠蔽し特定の型・パッケージに強く依存したコードを書けなくすることができます。
要は属性の直接参照、編集をできなくすることで、コードの途中での意図せぬ属性の参照や変更を防げる。
また、インターフェース越しでしか操作できないので、例えばNakamiという属性をAnkoに変えたとしても、GetNakami()をインターフェース越しに使ってるmain .go
でコード修正をする必要がない。
まぁ、普通にrubyでコード書いてるときもよくやることで、使い手側に影響が起きないように引数の数
とメソッド名
を固定して改修を進めるような場面を思い浮かべた。
# はじめはこういうクラス定義だったのが
class User
def initialize(name)
@name = name
end
def name_with_san
return "#{@name}さん"
end
end
---
# その後こういうクラス定義に変わったとしても、
class User
def initialize(onamae)
@onamae = "#{@onamae}さん"
end
def name_with_san
return @onamae
end
end
---
# 変更前のname_with_sanの使い方がこうなのに対して、
user = User.new("山田") # 変更前は内部的に `@name` を使ってる
p user.name_with_san # ⇒ 山田さん
# 変更後のname_with_sanの使い方も特に変える必要がない
user = User.new("山田") # 変更後は内部的に `@onamae` を使ってる
p user.name_with_san # ⇒ 山田さん
見づらいわかりづらいけど、書いてあること自体はinterfaceというかDIP(依存性逆転の原則)の理解によい