はじめに
自分で書いてしまった、またはどこかで見かけた💩コードをデフォルメして紹介します。
今回はGolangのインターフェースに関する💩コードです。
今回の💩コード
WEBアプリケーションです。
HTTPリクエスト/レスポンスのハンドリングと、ビジネスロジックを分けるために(多分)、endpoint
とservice
パッケージが作成されています。
package endpoint
import "service"
type SomeEndpoint struct {
svc service.SomeServiceInterface
}
func NewSomeEndpoint(svc service.SomeServiceInterface) SomeEndpoint {
return SomeEndpoint{
svc: svc,
}
}
package service
type SomeServiceInterface interface {
CreateRecord() error
}
type SomeService struct{}
func NewSomeService() SomeService {
return SomeService{}
}
func (s SomeService) CreateRecord() error {
return nil
}
解説
たまによく見かけますが何のために存在しているかわからないインターフェースです。
インターフェースを使わずに次のように書いているのと同じです。
package endpoint
import "service"
type SomeEndpoint struct {
svc service.SomeService
}
func NewSomeEndpoint(svc service.SomeService) SomeEndpoint {
return SomeEndpoint{
svc: svc,
}
}
package service
type SomeService struct{}
func NewSomeService() SomeService {
return SomeService{}
}
func (s SomeService) CreateRecord() error {
return nil
}
書いてる人も何のために書いてるかわかってないような気がしてなりません。
しかしながらインターフェースを使うべきではないというわけではありません。
今回の場合、endpoint
の方にインターフェースを作成するべきです。
package endpoint
type Service interface {
CreateRecord() error
}
type SomeEndpoint struct {
svc Service
}
func NewSomeEndpoint(svc Service) SomeEndpoint {
return SomeEndpoint{
svc: svc,
}
}
import "service"
の記述がなくなり、service
パッケージへ依存しなくなったのがわかるかと思います。
service
パッケージの方ははじめからendpoint
パッケージへ依存していないため、それぞれが互いの内部処理を気にすることなく実装を進めることができます。また、このようにしておくと、Service
インターフェースを実装したモックを作成し、endpoint
パッケージ単体のユニットテストを作成することができます。
おわりに
「DIP」や「抽象に依存しろ」のようなキャッチーなフレーズでインターフェースの存在は知っているものの使い方よくわからないという人、たまによく見ます。
プログラミング用語のインターフェースはとっつきにくい印象ですが、ソフトウェアデザインのユーザーインターフェース(UI)という言葉は割と広い層に受け入れられてきている印象があります。コンテキストは違いますが、インターフェースという概念は同じように思えます。
インターフェースという言葉の意味から立ち返ってみるとプログラミングにおけるインターフェースの使い方も見えてくるかもしれません。