[Tucker의 Go 언어 프로그래밍] 20장 인터페이스

Coen·2023년 11월 1일
1

tucker-go

목록 보기
15/18
post-thumbnail

20 인터페이스

20.1 인터페이스

  • interface를 직역하면 상호작용면

  • 인터페이스를 이용하면 메서드 구현을 포함한 구체화된 객체가 아닌 추상화된 객체로 상호작용 가능

20.1.1 인터페이스 선언

type DuckInterface interface {
	Fly()
    Walk(distance int) int
}

인터페이스 메서드 집합의 유의사항
1. 메서드는 반드시 메서드명이 있어야 한다.
2. 매개변수와 반환이 다르더라도 이름이 같은 메서드는 있을 수 없다.
3. 메서드 구현을 포함하지 않는다.

type Human interface {
	Speak() string
}

type Korean struct {
	Name string
	Age  int
}

type Japanese struct {
	Name string
	Age  int
}

func (k Korean) Speak() string {
	return "안녕하세요"
}

func (k Japanese) Speak() string {
	return "こんにちは"
}

func Test_interface(t *testing.T) {
	var human Human
	human = Korean{"Coen", 34}
	t.Log(human.Speak())
	human = Japanese{"Coen", 34}
	t.Log(human.Speak())
}
  • 위에서 Human 이라는 인터페이스를 선언

  • Korean과 Japanese라는 리시버에 Speak()라는 메소드를 구현

  • Human에 Korean과 Japanese라는 구현체를 대입할 수 있다.

  • 각 구현체에서 메서드를 호출하면 구현한 반환값이 반환된다.

20.2 인터페이스 왜 쓰나?

  • 객체지향 프로그래밍에서 아주 중요한 역할을 한다.
  • 구체화된 객체가 아닌 인터페이스만 가지고 메서드를 호출할 수 있기 때문에 큰 코드 수정 없이 필요에 따라 구체화된 객체를 바꿔서 사용할 수 있다.
  • 변경 요청에 유연하게 대처 가능

20.2.1 추상화 계층

  • 내부 동작을 감춰서 서비스를 제공하는 쪽과 사용하는 쪽 모두에게 자유를 주는 방식을 추상화라고 하는데, 인터페이스는 추상화를 제공하는 추상화 계층이다.
  • 자동차 시동을 켤 때, 내부에서 어떤 방식으로 동작해 시동이 걸리는지 몰라도 버튼만 누르면 시동이 걸리는데, 이렇게 추상화 계층을 이용해 서로 결합을 끊는 것을 디커플링이라고 한다.
  • 추상화를 거치면 내부 구현을 알 수 없고 오직 인터페이스의 메서드 집합만 알 수 있다.

20.3 덕 타이핑

  • Go 언어에서는 어떤 타입이 인터페이스를 포함하고 있는지 여부를 결정할 때 덕 타이핑 방식을 사용한다.
  • 타입 선언 시 인터페이스 구현 여부를 명시적으로 나타낼 필요가 없다.
  • 인터페이스에 정의된 메서드만 포함한다면 구현한 것으로 본다.

20.3.1 서비스 사용자 중심 코딩

  • 덕 타이핑을 사용하면 인터페이스 판단을 사용자가 할 수 있어 서비스 사용자 중심 코딩이 가능하다(?)

20.4 인터페이스 기능 더 알기

  • 포함된 인터페이스
  • 빈 인터페이스
  • 인터페이스 기본값

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

type Reader interface {
	Read()
	Close()
}

type Writer interface {
	Write()
	Close()
}

type ReadWriter interface {
	Reader
	Writer
}

type People struct {
	Name string
}

func (h People) Read() {
	fmt.Println("Read")
}

func (h People) Write() {
	fmt.Println("Write")
}

func (h People) Close() {
	fmt.Println("Close")
}

func Test_interfaceIncludeInterface(t *testing.T) {
	var readWriter ReadWriter
	var writer Writer
	var reader Reader
	readWriter = People{"Coen"}
	writer = People{"Coen"}
	reader = People{"Coen"}
	readWriter.Read()
	writer.Write()
	reader.Close()
}
  • 세가지 메서드를 모두 구현한 구조체는 위 세가지 interface를 모두 사용 가능하다.

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

  • interface{}는 메서드를 가지고 있지 않은 빈 인터페이스다.
  • 모든 타입이 빈 인터페이스로 사용 가능.
func GuessMyType(i interface{}) string {
	switch v := i.(type) {
	case int:
		return "int"
	case string:
		return "string"
	case bool:
		return "bool"
	default:
		return fmt.Sprintf("%T", v)
	}
}

func Test_emptyInterface(t *testing.T) {
	t.Log(GuessMyType(1))
	t.Log(GuessMyType("1"))
	t.Log(GuessMyType(true))
	t.Log(GuessMyType(1.1))
	t.Log(GuessMyType([]int{1, 2, 3}))
}

20.4.3 인터페이스 기본값 nil

func Test_interfaceIsNil(t *testing.T) {
	var readWriter ReadWriter
	var people People
	t.Log(readWriter) //nil
	t.Log(people) //{}
}

20.5 인터페이스 변환하기

  • 인터페이스 변수를 타입 변환을 통해 구체화된 다른 타입이나 다른 인터페이스로 변환할 수 있다.

20.5.1 구체화된 다른 타입으로 타입 변환하기

  • 인터페이스 변수를 다른 구체화된 타입으로 타입 변환 가능
  • 인터페이스를 본개의 구체화된 타입으로 복원할 때 주로 사용한다.
func interfaceToStruct(readWriter interface{}) {
	if t, ok := readWriter.(People); ok {
		log.Println(t)
	} else {
		log.Println("Its not People")
	}

}

func Test_interfaceToStruct(t *testing.T) {
	var readWriter ReadWriter
	readWriter = People{Name: "Coen"}
	interfaceToStruct(readWriter) //{Coen}
	interfaceToStruct(Korean{}) //Its not People
}
  • 위 예시에서는 return을 두개 받도록 하여 두번째 요소에 성공여부를 받아 타입변경 실패시에도 문제없게 돌아간다. 하지만 return element를 하나만 받도록 생성했을 때 실패하면 Panic이 발생한다. 주의 필요

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

  • 인터페이스 변환을 통해 구체화된 타입 뿐 아닌 다른 인터페이스로 타입 변환 가능.
  • 이때는 구체화된 타입으로 변환과는 달리 변경되는 인터페이스가 변경 전 인터페이스를 포함하지 않아도 된다. 단 인터페이스가 가리키고 있는 실제 인스턴스가 변환하고자 하는 다른 인터페이스를 포함해야 한다.

20.5.3 타입 변환 성공 여부 반환

  • 20.5.1 참고~
profile
백엔드 프로그래머

0개의 댓글