Go는 일반적으로 사용하는 타입 중심의 subclass개념을 제공하지 않는다.
대신 struct나 인터페이스 안에 타입을 임베딩 시키면서 구현체의 일부를 ‘빌리는’ 것은 가능하다.
인터페이스 임베딩은 매우 간단한데, io.Reader와 io.Writer 인터페이스의 정의를 살펴보자.
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
io 패키지는 다수의 인터페이스들을 다수의 메서드를 구현할 수 있는 객체를 생성하는데 사용하도록 외부에 제공한다.
io.ReadWriter는 Read와 Write를 모두 가지고 있는데, 이 두 메서드를 나열해서 ReadWriter를 정의할 수도 있지만,
더 쉽고 기억하기 좋은 방법은 두 개의 인터페이스를 임베딩시켜서 하나의 인터페이스를 생성하는 것이다.
// ReadWriter는 Reader와 Writer 인터페이스를 조합하는 인터페이스이다.
type ReadWriter interface {
Reader
Writer
}
Reader가 하는 일과 Writer의 하는 일을 ReadWriter 모두 할 수 있음을 나타내는 임베드 된 인터페이스들의 조합이다.
오직 인터페이스만이 인터페이스에 끼워질 수 있음
기본적으로 똑같은 아이디어를 struct에도 적용할 수 있는데, 그 영향은 훨씬 광범위함
예를 들어 bufio패키지에는 bufio.Reader, bufio.Writer 두 struct 타입이 있는데, 이는 모두 io 패키지에 있는 인터페이스를 구현하고 있다.
그런데 bufio는 또 버퍼를 내재한 reader/writer를 구현하기도 하는데, 임베딩을 이용하여 reader, writer를 하나의 struct에 조합하는 것: struct안에 타입들을 나열하되, 필드 이름을 주지 않는 방식
// ReadWriter Reader와 Writer에 대한 포인터들을 저장한다.
// io.ReadWriter를 구현한다.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
임베드된 요소들은 struct를 가리키는 포인터들이고, 사용 전 초기화가 필요하다.
또 다른 방법으로 아래와 같이 struct내에 선언할 수 있다.
type ReadWriter struct {
reader *Reader
writer *Writer
}
이렇게 하면 io를 충족시키고, reader 와 writer가 가지고 있는 메서드를 사용하기 위해 포워딩 메서드를 따로 제공해야 한다.
func (rw *ReadWriter) Read(p []byte) (n int, err error) {
return rw.reader.Read(p)
}