Go 프로그래밍 - 인터페이스

beno·2021년 7월 17일
0

golang

목록 보기
8/8

추상화된 객체로 상호작용하기 위한 방법

1. 선언하기

type DuctInterface interface {
  Fly()
  Walk(distance int) int
}
  1. 메서드는 반드시 메서드명이 있어야 한다
  2. 매개변수와 반환이 다르더라도 이름이 같은 메서드는 있을 수 없다
  3. 인터페이스에서는 메서드 구현을 포함하지 않는다.
사용 예시
package main

import "fmt"

type Stringer interface {
  String()
}

type Student struct {
  Name string
  Age int
}

func (s Student) String() {
  fmt.Printf("안녕 내 이름은 %s이고 나이는 %d살이야!\n", s.Name, s.Age)
}

func main() {
  student := Student{"철수", 31}
  var stringer Stringer
  stringer = student

  stringer.String()
}

2. 인터페이스는 왜 쓰는가?

타입 확장에 유연하게 대응하기 위함

문제 상황

  • 아래와 같이 동물 농장에 강아지만 있는 상황을 가정해보자.
package main

import "fmt"

type Dog struct {
  Name string
}

func (d Dog) Sound() {
  fmt.Printf("[%s]: 멍멍\n", d.Name)
}

func MakeSound(dogs []Dog) {
  for _, d := range dogs {
    d.Sound()
  }
}

func main() {
  dogs := []Dog{}
  dogs = append(dogs, Dog{"멍멍이"}, Dog{"왈왈이"})
  MakeSound(dogs)
}
  • 갑자기 고양이가 추가되어 새로운 타입 정의가 필요해졌다
type Cat struct {
  Name string
}

func (c Cat) Sound() {
  fmt.Printf("[%s]: 야옹\n", c.Name)
}
  • 하지만 기존 MakeSound 함수는 Dog 타입만 인자로 받고 있었기 때문에 Cat에 대한 대응이 불가능하다
// Dog 타입 슬라이스만 받음..
func MakeSound(dogs []Dog) {
  for _, d := range dogs {
    d.Sound()
  }
}

해결 방안

위 케이스의 Sound 같은 공통 메서드를 인터페이스를 만든다

package main

import "fmt"

type Animal interface {
  Sound()
}

type Dog struct {
  Name string
}

func (d Dog) Sound() {
  fmt.Printf("[%s]: 멍멍\n", d.Name)
}

type Cat struct {
  Name string
}

func (c Cat) Sound() {
  fmt.Printf("[%s]: 야옹\n", c.Name)
}

func MakeSound(animals []Animal) {
  for _, a := range animals {
    a.Sound()
  }
}

func main() {
  animals := []Animal{}
  animals = append(animals, Dog{"멍멍이"}, Dog{"왈왈이"}, Cat{"야옹이"})
  MakeSound(animals)
}

3. 인터페이스 기능 더 알기

3.1 인터페이스를 포함하는 인터페이스

type A interface {
	AA() (int, error)
	CC() error
}

type B interface {
	BB() (int, error)
	CC() error
}

type C interface {
	A
	B
}
  • CC() error 가 겹치지만 같은 메서드 형식이므로 문제 없음

3.2 빈 인터페이스를 interface{}를 인수로 받기

어떤 값이든 받을 수 있는 함수, 메서드, 변숫값을 만들때 사용

package main

import (
	"fmt"
)

func TypeChecker(v interface{}) {

	switch t := v.(type) {
	case int:
		fmt.Printf("v is int %d\n", v)
	case string:
		fmt.Printf("v is string %s\n", v)
	case float64:
		fmt.Printf("v is float64 %f\n", v)
	default:
		fmt.Printf("%T:%v type is not acceptable\n", t, t)
	}
}

type Person struct {
	Name string
}

func main() {
	TypeChecker(32)
	TypeChecker(0.5)
	TypeChecker("hello")
	TypeChecker(Person{"철수"})
}

4. 인터페이스 변환하기

4.1 인터페이스를 구체화된 타입으로 변환

package main

import (
	"fmt"
)

type AInterface interface {
	DoSomething()
}

type AStruct struct {
	Name string
}

func (a AStruct) DoSomething() {
	fmt.Println("hello")
}

type BStruct struct {
	Name string
	Age  int
}

func (b BStruct) DoSomething() {
	fmt.Println("hello")
}

func TypeConversion(aInterface AInterface) {
	b := aInterface.(*BStruct)
	fmt.Println(b)
}

func main() {
	a := &BStruct{"철수", 31}
	TypeConversion(a) // 타입 변환 성공
    
    	b := &AStruct{"철수"}
    	TypeConversion(b)
    	// > b가 가리키는 타입은 *AStruct이지만 *BStruct로 타입 변환하려고 했기 때문에 런타임 에러 발생
}

4.2 다른 인터페이스로 타입 변환하기

  • 아래와 같은 구조일때 BInterface와 AInterface는 서로 타입 변환이 가능하다

package main

import (
	"fmt"
)

type AInterface interface {
	DoSomething()
}

type BInterface interface {
	DoNothing()
}

type AStruct struct {
	Name string
}

func (a AStruct) DoSomething() {
	fmt.Println("hello")
}

func (a AStruct) DoNothing() {
	fmt.Println("hello")
}

func TypeConversion(bInterface BInterface) {
	a := bInterface.(AInterface)
	fmt.Println(a)
}

func main() {
	a := &AStruct{"철수"}
	TypeConversion(a)
}

4.3 인터페이스 변환 시 예외처리하는 방법

var a Interface
if t, ok := a.(ConcreteType); ok {
	...
}
  • 변환에 성공한 경우
    • t: 타입 변환된 결과
    • ok: true
  • 변환에 실패한 경우
    • t: ConcreteType의 기본값
    • ok: false
profile
마음은 여기에. 시선은 저 멀리.

0개의 댓글

관련 채용 정보