Go言語の種類とインターフェースの関係

Go言語の種類とインターフェースの関係

 
 
Go 言語の型とインターフェイスの間には 1 対多および多対 1 の関係があります。読者が複雑な環境におけるインターフェイスと型の間の実装関係を理解し​​やすくするために、これらの一般的な概念を以下にリストします。

 

型は複数のインターフェイスを実装できます

型は複数のインターフェイスを同時に実装できますが、インターフェイスは互いに独立しており、互いの実装を認識しません。

ネットワーク上の 2 つのプログラムは双方向通信接続を通じてデータを交換し、接続の一端はソケットと呼ばれます。ソケットは、ファイルと同様に、データの読み取りと書き込みを同時に行うことができます。したがって、開発では、ファイルとソケットの両方の読み取りおよび書き込み機能が、リーダーの独立した概念に抽象化されます。

ソケットはファイルと同様、使用後にリソースを解放する必要があります。

インターフェイスを使用して、データを書き込むことができ、閉じる必要があるソケットの特性を記述します。次のコードを参照してください。

 type Socket struct {
}

func (s *Socket) Write(p []byte) (n int, err error) {
    return 0, nil
}

func (s *Socket) Close() error {
    return nil
} 

Socket 構造の Write() メソッドは、io.Writer インターフェイスを実装します。

type Writer interface {
    Write(p []byte) (n int, err error)
} 

同時に、Socket 構造は io.Closer インターフェイスも実装します。

type Closer interface {
    Close() error
} 

Socket によって実装された Writer インターフェイスのコードを使用すると、Writer インターフェイスの実装者が Closer インターフェイスの特性を備えているかどうかを知る必要はありません。同様に、次の図に示すように、Closer インターフェイスを使用するコードは、Socket が Writer インターフェイスを実装していることを認識しません。

コード内で Socket 構造を使用して実装された Writer インターフェイスと Closer インターフェイスのコードは次のとおりです。

 // io.Writerを利用するコードで、Socketとio.Closerの存在を知らない
func usingWriter(writer io.Writer){
    writer.Write(nil)
}

// io.Closerを利用するコードで、Socketとio.Writerの存在を知らない
func usingCloser(closer io.Closer) {
    closer.Close()
}

func main() {

    // Socketのインスタンス化
    s := new(Socket)

    usingWriter(s)

    usingCloser(s)
} 

usingWriter() と usingCloser() は完全に独立しており、お互いの存在を知りません。また、使用するインターフェイスが Socket によって実装されていることも知りません。

複数の型が同じインターフェイスを実装できる

インターフェイスのメソッドは、必ずしも型によって完全に実装される必要はなく、インターフェイスのメソッドは、その型に他の型または構造を埋め込むことによって実装できます。つまり、ユーザーは、特定のインターフェイスのメソッドが型によって完全に実現されるか、構造に埋め込まれ、それを組み合わせて実現される複数の構造によって完全に実現されるかどうかを気にしません。

Serviceインターフェースでは、サービスを開始するメソッド(Start())と、ログを出力するメソッド(Log())の2つのメソッドが定義されています。 Serviceの実装にはGameService構造体を使用します。GameService自身の構造体ではStart()メソッドのみ実装でき、ServiceインターフェースのLog()メソッドはログを出力できるロガー(Logger)によって実装されているため、 GameService をカプセル化するか、もう一度再実装する必要があります。したがって、Logger を GameService に埋め込むことを選択すると、コードの冗長性を最大限に回避し、コード構造を簡素化できます。詳細な実装プロセスは次のとおりです。

 // 1つのサービスは、開始およびログの書き込み機能を備える必要があります
type Service interface {
    Start() // サービスを開始します
    Log(string) // ログを出力します
}

// ロガー
type Logger struct {
}

// ServiceのLog()メソッドを実装する
func (g *Logger) Log(l string) {

}

// ゲームサービス
type GameService struct {
    Logger // ロガーを埋め込む
}

// ServiceのStart()メソッドを実装する
func (g *GameService) Start() {
} 

コードの説明は次のとおりです。

  • 行 2 はサービス インターフェイスを定義しており、サービスは Start() メソッドと log メソッドを実装する必要があります。
  • 8行目ではログを出力できるロガー構造体を定義しています。
  • 12行目、LoggerにLog()メソッドを追加し、同時にServiceのLog()メソッドも実装します。
  • 17 行目は GameService 構造を定義します。
  • 18行目、GameServiceにLoggerロガーを埋め込み、ログ機能を実現します。
  • 22 行目では、GameService の Start() メソッドが Service の Start() メソッドを実装しています。

この時点で、GameService をインスタンス化し、そのインスタンスをサービスに割り当てます。コードは次のとおりです。

var s Service = new(GameService)
s.Start()
s.Log(“hello”) 

s では、Start() メソッドと Log() メソッドを使用できます。Start() は GameService によって実装され、Log() メソッドは Logger によって実装されます。

 

「 Go言語の種類とインターフェースの関係」についてわかりやすく解説!絶対に観るべきベスト2動画

一緒に学ぶGo言語入門 ~その9:メソッドとインターフェース(前編)~
Go言語で何ができるの?どこで使われてる?現役エンジニアが解説