프로그래밍/golang
ultimate in go (4)- data structure(interface)
by 나도한강뷰
2023. 1. 23.
interface
값이 없는 타입
type reader interface {
read(b []byte) (int, error) // (1)
}
type reader interface {
read(i int) ([]byte, error) // (2)
}
- []byte값을 통해서 반환하게 되면 메소드 호출마다 slice를 만들게 되고 반복적으로 힙메모리에 저장하게 됨으로 효율이 좋지 못하다
concrete type
- 매소드를 가질 수 있는 모든 타입/
- 사용자 정의 타입만이 메소드를 가질수 있다.
- 남이 만들어 놓은 타입을 동일 패키지내에서 선언없이는 메소드를 가질 수 없음
인터페이스를 이용하여서 메소드를 호출할때, 메소드와 인터페이스간의 관계
type file struct {
name string
}
type pipe struct {
name string
}
func (file) read(b []byte) (int, error) {
s := "<rss><channel><title>Going Go Programming</title></channel></rss>"
copy(b, s)
return len(s), nil
}
func (pipe) read(b []byte) (int, error) {
s := `{name: "hoanh", title: "developer"}`
copy(b, s)
return len(s), nil
}
f := file{"data.json"}
p := pipe{"cfg_service"}
func retrieve(r reader) error {
data := make([]byte, 100)
len, err := r.read(data)
if err != nil {
return err
}
fmt.Println(string(data[:len]))
return nil
}
retrieve(f)
retrieve(p)
- 이런 상황에서 f와 reader라는 interface는 어떻게 작동하는가
- reader interface는 2개의 word를 가지고 있고, 첫번째 word는 itable이라는 값을 가르키고
- 두번째 word는 f의 복사본을 가르킨다.
- itable의 첫번째값은 저장된 데이터 타입을 의미
- 두번째 값은 함수 포인터의 모체, 인터페이스를 통해서 정확한 메소드를 호출하기위해서 사용
- 이렇게 f와 p는 각각 다른 read를 호출하게 되는데, 둘의 메소드 구현은 완전히 디커플링 되어있고,
- retrieve가 interface를 통해서 값을 받는것은 go에서 보여주는 최고 수준의 디커플링이다.
포인터 리시버를 이용한 인터페이스
type notifier interface {
notify()
}
type printer interface {
print()
}
type user struct {
name string
email string
}
func (u user) print() {
fmt.Printf("My name is %s and my email is %s\n", u.name, u.email)
}
func (u *user) notify() {
fmt.Printf("Sending User Email To %s<%s>\n", u.name, u.email)
}
func (u *user) String() string {
return fmt.Sprintf("My name is %q and my email is %q", u.name, u.email)
}
u := user{"Hoanh", "hoanhan@email.com"}
type duration int
func (d *duration) notify() {
fmt.Println("Sending Notification in", *d)
}
duration(42).notify()
func sendNotification(n notifier) {
n.notify()
}
- sendNotification(u)를 하면, 에러가나온다. 왜냐하면 u는 포이터 메소드였기 때문이다.
- 값 리시버 매소드
- 포인터 리시버 매소드
- 앞서서 매소드를 사용할때 문제가 없었던것은, 구체적인 값을 이용하여서 호출하기때문인데,
- 인터페이스를 통해서 매소드를 호출할때는 구체적인 값을 통해서 호출하는 것이 아니기때문에
- 문제가 생길 수 있으므로 그것을 방지하는 것이다.
- 그것은 duration(42).notify()같은 경우에, 값은 있지만, 주소값은 없는 형태가 된다.
- 그러므로 포인터가 없기때문에 에러가 발생하는 가능성이 생기고, 그에따라서 포인터로 선언된 매소드는
- 포인터로만 사용할 수 있다(인터페이스를 통하는 경우)