상세 컨텐츠

본문 제목

Go 언어에서 인터페이스

Go lang

by techbard 2022. 3. 13. 18:50

본문

반응형
  1. 구조체 생성
  2. 구조체와 연결할 메서드를 인터페이스로 정의
  3. 결과값은 맵에 저장 (맵은 참조형이라 함수에 그대로 넘길 수 있다.)
  4. 구조체 요소의 개수를 세는 메서드도 구조체로 인터페이스 정의해서 처리할 수 있다?
package main

import (
	"fmt"
)

func main() {
	e1 := employee{
		name:    "mike",
		age:     31,
		salary:  1000,
		wage:    0,
		emptype: "regular",
	}

	e2 := employee{
		name:    "jane",
		age:     28,
		salary:  500,
		wage:    0,
		emptype: "contract",
	}

	e3 := employee{
		name:    "ralf",
		age:     22,
		salary:  0,
		wage:    100,
		emptype: "parttimer",
	}

	e4 := employee{
		name:    "danny",
		age:     21,
		salary:  0,
		wage:    110,
		emptype: "parttimer",
	}

	e5 := employee{
		name:    "sunny",
		age:     32,
		salary:  0,
		wage:    120,
		emptype: "parttimer",
	}

	monthlySalary := map[string]int{
		"regular":   0,
		"contract":  0,
		"parttimer": 0,
	}

	var empOP empOperation

	emps := []employee{e1, e2, e3, e4, e5}
	for _, e := range emps {
		empOP = e                      // 가능한 이유는 동일 메서드를 구현했기 때문에 (동일 시그니처)
		empOP.setSalary(monthlySalary) // Map은 참조형
	}

	empOP.getMonthlySalary(monthlySalary)
	empOP.getTotal(emps) // 인터페이스 통해 []employee 슬라이스에 접근
}

type empOperation interface {
	setSalary(map[string]int)
	getMonthlySalary(map[string]int)
	getTotal([]employee)
}

type employee struct {
	name    string
	age     int
	salary  int
	wage    int
	emptype string
}

func (e employee) setSalary(m map[string]int) {
	switch e.emptype {
	case "regular":
		m["regular"] += e.salary
	case "contract":
		m["contract"] += e.salary
	case "parttimer":
		m["parttimer"] += e.wage
	}
}

func (e employee) getMonthlySalary(m map[string]int) {
	fmt.Println("monthly regulay salary:", m["regular"])
	fmt.Println("monthly contract salary:", m["contract"])
	fmt.Println("monthly parttimer salary:", m["parttimer"])
}

func (e employee) getTotal(emps []employee) {
	fmt.Println("total person:", len(emps))
}

// 결과
//
// monthly regulay salary: 1000
// monthly contract salary: 500
// monthly parttimer salary: 330
// total person: 5
  1. 인터페이스로 여러 다른 종류의 변수를 묶어 처리
  2. 새로운 구조체를 추가하더라도 인터페이스만 구현하면 기존 코드 변경을 최소화할 수 있음
package main

import (
	"fmt"
)

func main() {
	i := ints{1, 2}
	j := floats{1.1, 2.2}
	k := ints{3, 4}

	cs := []calc{i, j, k}

	for _, c := range cs {
		show(c)
	}
}

type calc interface {
	plus()
}

type ints struct {
	x, y int
}

func (i ints) plus() {
	fmt.Println("int:", i.x+i.y)
}

type floats struct {
	x, y float64
}

func (f floats) plus() {
	fmt.Println("float:", f.x+f.y)
}

func show(c calc) {
	c.plus()
}

// 결과
//
// int: 3
// float: 3.3000000000000003
// int: 7

// 만일 새로운 구조체를 추가할 필요가 있고 인터페이스만 구현했다면
// 아래와 같이 그냥 추가만 하고 기존 코드는 최대한 수정하지 않아도 된다.

package main

import (
	"fmt"
)

func main() {
	i := ints{1, 2}
	j := floats{1.1, 2.2}
	k := ints{3, 4}
	l := uints{10000, 9999} // 추가

	cs := []calc{i, j, k, l}  // 추가

	for _, c := range cs {
		show(c)
	}
}

type calc interface {
	plus()
}

type ints struct {
	x, y int
}

func (i ints) plus() {
	fmt.Println("int:", i.x+i.y)
}

type floats struct {
	x, y float64
}

func (f floats) plus() {
	fmt.Println("float:", f.x+f.y)
}

type uints struct {  // 추가
	x, y uint64
}

func (u uints) plus() {  // 추가
	fmt.Println("uint:", u.x+u.y)
}

func show(c calc) {
	c.plus()
}

// 결과
//
// int: 3
// float: 3.3000000000000003
// int: 7
// uint: 19999
  1. 실용적이진 않지만 Go 언어에서 인터페이스를 사용해 만들어 본다.
  2. 구조체의 멤버를 가지고 개수를 세고, 합치는 결과를 출력한다.
  3. 구조체를 메서드의 리시버로 받고 count(), merge() 함수에서 int, string으로 리턴한다.
  4. 인터페이스 슬라이스에 각각을 담고 각각의 객체 (?)에 동일한 이름의 함수를 호출한다.
package main

import (
	"fmt"
	"strconv"
)

type ints struct {
	nums []int
}

func (i ints) count() (count int) {
	for i, _ := range i.nums {
		count = i
	}
	count++
	return
}

func (i ints) merge() (merge string) {
	for _, n := range i.nums {
		merge += strconv.Itoa(n)
	}
	return
}

type strs struct {
	txts []string
}

func (s strs) count() (count int) {
	for i, _ := range s.txts {
		count = i
	}
	count++
	return
}

func (s strs) merge() (merge string) {
	for _, t := range s.txts {
		merge += t
	}
	return
}

type operator interface {
	count() int
	merge() string
}

func main() {
	var o operator
	o = ints{[]int{1, 2, 3, 4, 5}}

	var ops []operator
	ops = append(ops, o)

	s := strs{[]string{"a", "b", "c"}}
	ops = append(ops, s)

	for _, op := range ops {
		fmt.Printf("%T count: %d\n", op, op.count())
		fmt.Printf("%T merge: %s\n", op, op.merge())
	}
}

// 결과
//
// main.ints count: 5
// main.ints merge: 12345
// main.strs count: 3
// main.strs merge: abc

  • -er 형태의 인터페이스 구조
package main

import (
	"fmt"
)

func main() {
	var w Writer = ConsoleWriter{}
	w.Write([]byte("Hello World!"))
}

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

type ConsoleWriter struct{}

func (cw ConsoleWriter) Write(data []byte) (int, error) {
	n, err := fmt.Println(string(data))
	return n, err
}

// 결과
//
// Hello World!

package main

import (
	"fmt"
)

func main() {
	// int type is defined in another package.
	myInt := IntCounter(0)
	var inc Incrementer = &myInt
	for i := 0; i < 10; i++ {
		fmt.Println(inc.Increment())
	}
}

type Incrementer interface {
	Increment() int
}

type IntCounter int

func (ic *IntCounter) Increment() int {
	*ic++
	return int(*ic)
}

// 결과
//
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10

Best Practices

- Use many, small interfaces
	* single method interfaces are some of the most powerful and flexible
		io.Writer, io.Reader, interface{}

- Don't export interfaces for types that will be consumed

- Do export interfaces for types that will be used by package

- Design functions and methods to receive interfaces whenever possible
반응형

관련글 더보기

댓글 영역