17.2. 프로그램언어 고(Go)의 인터페이스 상속

프로그램언어 고(Go)의 인터페이스 상속방법

Go 언어의 인터페이스 상속 방법에 대해 한글로 설명 드리겠습니다.

Go언어에서 인터페이스는 메서드의 시그니처(함수 프로토타입)만 정의할 수 있고, 구현부는 정의할 수 없습니다. 그래서 Go의 인터페이스는 duck typing을 사용합니다. 예를 들어 이런 인터페이스가 있다고 합시다.

type Reader interface {
    Read(p []byte) (n int, err error)
}

Reader 인터페이스는 Read 메서드의 시그니처만 정의했습니다. Read 메서드를 구현하는 구조체는 Reader 인터페이스를 만족합니다. 예를 들어 이런 File 구조체가 있다고 합시다.

type File struct {
    // 파일 구조체의 내용 
}

func (f *File) Read(p []byte) (n int, err error) {
    // 파일 읽기 구현
}

File 구조체가 Read 메서드를 구현했기 때문에 Reader 인터페이스를 만족합니다. 따라서 File을 Reader 인터페이스 타입의 변수에 집어넣을 수 있습니다.

var r Reader = &File{}

위와 같이 인터페이스 타입에 구현 구조체를 집어넣음으로써, 인터페이스 상속 효과를 낼 수 있습니다.

인터페이스 합성도 가능합니다. 예를 들어 Reader와 Writer 인터페이스가 있다면, 이 두 인터페이스를 합쳐 ReadWriter 인터페이스를 만들 수 있습니다.

type ReadWriter interface {
    Reader
    Writer
}

struct가 Reader와 Writer 인터페이스 모두를 구현한다면 ReadWriter 인터페이스도 자동으로 만족하게 됩니다. 인터페이스 합성은 Go언어 인터페이스의 큰 강점 중 하나입니다.

이 외에도 인터페이스는 빈 인터페이스(interface{})를 사용할 수 있습니다. 이는 어떤 타입이든지 저장 가능한 인터페이스입니다. 또한 타입 아셈(type assertion)을 사용하여 인터페이스의 실제 타입을 가져올 수 있습니다.

var i interface{} = "hello"

s := i.(string) // i 인터페이스의 실제 타입은 string

위와 같이 인터페이스의 실제 타입을 확인할 수 있습니다.

지금까지 Go언어의 인터페이스 상속 방법에 대해 간략히 설명드렸습니다. 인터페이스는 Go언어의 핵심적인 특성 중 하나로, 구조체와 함께 유연성 있는 코드 작성을 돕습니다. 예제와 함께 더 자세히 설명드리겠습니다.

고맙습니다.

프로그램언어 고(Go)에서의 인터페이스 사이의 상속과 구현 예제

프로그램언어 고(Go)에서 인터페이스는 메서드의 집합을 정의합니다. 인터페이스를 구현하는 타입은 그 인터페이스에서 정의한 모든 메서드를 구현해야 합니다.

인터페이스 상속의 예시는 다음과 같습니다.


type Reader interface {
    Read(b []byte) (n int, err error)
}

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

// ReadWriter는 Reader와 Writer 인터페이스를 모두 상속받음
type ReadWriter interface {
    Reader
    Writer
}

ReadWriter 인터페이스는 Reader와 Writer 인터페이스를 상속받아 Read와 Write 메서드를 모두 가지고 있습니다.

인터페이스 구현 예시는 다음과 같습니다.


type File struct {
    // 파일 구조체
}

func (f *File) Read(b []byte) (n int, err error) {
    // 파일 읽기 구현
}

func (f *File) Write(b []byte) (n int, err error){
    // 파일 쓰기 구현
}

var _ ReadWriter = (*File)(nil) // (*File)이 ReadWriter 인터페이스를 구현했음을 체크

File 구조체가 Reader와 Writer 인터페이스에 정의된 메서드들을 구현하였기 때문에, File은 ReadWriter 인터페이스도 구현한 것입니다.

var _ ReadWriter = (*File)(nil) 문장은 File 구조체가 ReadWriter 인터페이스를 제대로 구현했는지 컴파일 시점에 체크합니다.

이 예시를 통해 프로그램언어 고(Go)의 인터페이스 상속과 구현 방법을 확인할 수 있습니다. 인터페이스를 잘 활용하면 타입 사이의 관계를 유연하게 정의할 수 있습니다.

프로그램언어 고(Go)에서의 여러 인터페이스를 상속받는 방법

고(Go)언어에서 여러 인터페이스를 상속받는 방법을 설명드리겠습니다.

고(Go)언어에서는 다중 상속이 허용되지 않습니다. 하지만 인터페이스를 이용하면 다수의 인터페이스를 구현할 수 있습니다. 인터페이스는 메서드 시그니처만 정의하고 구현부는 없는 타입입니다.

struct나 타입이 여러 인터페이스를 구현하면 그 인터페이스에 정의된 모든 메서드를 구현해야 합니다. 예를 들어 아래와 같이 Reader와 Writer 인터페이스를 구현합니다.

type Reader interface {
    Read(b []byte) (n int, err error)
}

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

type File struct {
    //...
}

func (f *File) Read(b []byte) (n int, err error) {
    //...
}

func (f *File) Write(b []byte) (n int, err error){
    //...  
}

File 구조체는 Reader와 Writer 인터페이스를 모두 구현했습니다. 이를 통해 File은 Reader, Writer 인터페이스 타입의 값으로도 사용될 수 있습니다.

var r Reader = new(File) 
var w Writer = new(File)

위 예제코드와 같이 File 포인터를 Reader, Writer 인터페이스 변수에 할당할 수 있습니다.

인터페이스 상속을 이용하면 타입 결합도를 낮출 수 있고 복잡한 객체를 부품화하여 만드는 데 도움이 됩니다. 필요한 기능만 인터페이스로 추출하여 사용하기 때문입니다.

고(Go)언어 인터페이스를 이용한 다중 상속은 위와 같이 실사용할 때 아주 유용하게 사용될 수 있습니다. 꼭 필요한 경우가 아니면 인터페이스의 과다 사용은 바람직하지 않다고 생각합니다.

프로그램언어 고(Go)에서의 상속된 인터페이스 메서드 사용 방법

고(Go)언어에서 인터페이스 메서드 사용 방법을 설명드리겠습니다.

고(Go)언어에서 인터페이스를 정의하고, 구조체에서 해당 인터페이스를 구현할 수 있습니다. 그리고 인터페이스 타입의 변수를 선언해서 구조체 인스턴스를 할당하면 인터페이스에서 정의한 메서드를 구조체 인스턴스를 통해 사용할 수 있습니다.

예를 들어 다음과 같은 인터페이스를 정의할 수 있습니다.

type Shape interface {
    area() float64
}

여기서 area() 메서드를 정의했습니다. 그리고 구조체를 다음과 같이 정의할 수 있습니다.

type Rectangle struct {
    width, height float64 
}

func (r Rectangle) area() float64 {
    return r.width * r.height
}

여기서 Rectangle 구조체는 Shape 인터페이스에서 정의한 area() 메서드를 구현하고 있습니다.

그러면 Shape 인터페이스 타입의 변수를 선언하고 Rectangle 인스턴스를 할당할 수 있습니다.

var s Shape
r := Rectangle{width: 3, height: 4}
s = r

area := s.area() // Rectangle의 area() 메서드 호출

위 코드에서 Shape 인터페이스 타입의 s 변수를 선언하고, Rectangle 인스턴스를 할당했습니다.
이제 s 변수를 통해 Rectangle의 area() 메서드를 호출할 수 있습니다.

즉, 인터페이스 타입으로 선언된 변수를 통해 해당 인터페이스의 메서드를 구현한 구조체의 메서드를 호출할 수 있다는 것입니다.

이를 통해 구조체의 실제 타입을 인터페이스 타입으로 추상화하여 메서드를 호출할 수 있도록 할 수 있습니다.

이는 고(Go)언어 인터페이스의 큰 장점 중 하나입니다.

위 예제 코드에 대한 더 자세한 설명은 아래와 같습니다.

1. Shape 인터페이스 정의
– area() 메서드 정의
2. Rectangle 구조체 정의
– Shape 인터페이스의 area() 메서드 구현
3. Shape 인터페이스 타입의 s 변수 선언
4. Rectangle 인스턴스 생성하여 s 변수에 할당
5. s 변수를 통해 Rectangle의 area() 메서드 호출

이렇게 인터페이스와 구조체를 사용하면 타입에 의존하지 않고 메서드를 호출할 수 있습니다.

고(Go)언어 Interface 사용은 다형성을 위해 매우 중요한 기능이므로 꼭 숙지해야 할 것 같습니다.

이상 설명 드린 내용이 인터페이스 메서드 사용 방법을 이해하는데 도움이 되었기를 바랍니다.

프로그램언어 고(Go)에서의 인터페이스 상속과 다형성의 활용

고(Go)언어에서 인터페이스는 메서드의 집합을 정의한다고 보시면 됩니다. 구조체가 인터페이스를 구현하면 그 구조체는 반드시 인터페이스에서 정의한 메서드들을 가지고 있어야 합니다.

이를 통해 다형성을 활용할 수 있습니다. 예를 들어 이러한 인터페이스가 있다고 가정해보겠습니다.

type Reader interface {
    Read(b []byte) (n int, err error)
}

type File struct {
    // ...
}

func (f *File) Read(b []byte) (n int, err error) {
    // ... 실제 파일 읽기 구현 ...
}

type Pipe struct {
    // ...
}

func (p *Pipe) Read(b []byte) (n int, err error) {
    // ... 실제 파이프 읽기 구현 ... 
}

여기서 Reader 인터페이스는 Read 메서드를 정의하고 있습니다.
File과 Pipe 구조체가 이 인터페이스를 구현했기 때문에 Read 메서드를 가지고 있습니다.

이제 이 인터페이스를 받는 함수를 다음과 같이 정의할 수 있습니다.

func readData(reader Reader) {
    buf := make([]byte, 1024)
    for {
        n, err := reader.Read(buf)
        // 읽은 데이터 처리
    }
}

readData 함수는 Reader 인터페이스를 参数로 받습니다.
따라서 이 함수에 File 구조체나 Pipe 구조체를 전달할 수 있습니다.

f := File{/* ... */} 
p := Pipe{/* ... */}

readData(&f)
readData(&p)

이처럼 인터페이스와 구조체를 이용해서 다형성을 활용할 수 있습니다.
구조체가 해당 인터페이스를 구현하고 있다면 인터페이스 타입으로 해당 구조체의 인스턴스를 전달할 수 있게 됩니다.

인터페이스의 가장 큰 장점은 이러한 다형성을 활용해서 구조체의 코드를 해당 인터페이스 타입으로 추상화시킬 수 있다는 점입니다.
이로써 보다 유연한 코드를 작성할 수 있습니다.

고맙습니다.

Leave a Comment