10. Method And Interface
: 개인적으로 객체지향에 익숙한 나로써는 재미있는 부분이었음.
: composition팬턴으로 작은 단위의 자료형을 만들고, 한가지의 동작을 구현하는 interface를 사용한다.
: 자바는 객체지향 언어로 객체를 바라보지만, Go는 객체를 잘게 부슨 method(기능)의 작은 단위로 기능을 바라본다.
: 덕 타이핑으로 오리를 만든다고 예를 들면, 오리가 "꽥꽥"소리를 내는 것을 "오리"로 규정하고, "꽥꽥"소리를 내는것은 모두 "오리"로 봐버리는 것과 동일하다. 하지만, 자바에서는 하나의 메소드로 "꽥꽥"소리만 낸다고 해서 "오리"가 될수는 없다. 모든 오리의 메소드와 속성이 규정되어야 된다. 엄밀이 따지면 Go는 "오리"를 표현하는 것이 아니라 "꽥꽥"소리를 내는 메소드를 인터페이스로 만들고, 그 Method를 합성하여 어떠한 객체를 형성해 간다, 재사용성을 높이고 효율성을 높인것이다.
10.1. 메소드 ( Method )
: Go에서는 클래스가 없다. 하지만, 메소드를 구조체(struct)/타입(type)에 임베디드 할수 있다.(하지만, 기본 타입이나, 다른 패키지의 타입에는 붙일수 없다.) method receiver는 func키워드와 메소드의 이름 사이에 인자로 들어간다.
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {// *Vertex 는 포인트 타입 리시버
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := &Vertex{3, 4}
fmt.Println(v.Abs()) //출력 : 5
}
package main
import (
"fmt"
"math"
)
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
func main() {
f := MyFloat(-math.Sqrt2)
fmt.Println(f.Abs())
}
10.1.1. 포인터 리시버를 가지는 메소드
: 리시버를 포인터를 사용하여 선언한 형태
- 사용 이유
- 메소드가 호출될때 마다 값이 복사되는 것을 방지하기 위함.
- 메소드에서 리시버 포인터가 가르키는 값을 수정 하기 위함.
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f //리시버 포인터를 사용하여 가르키는 값을 수정 할수 있다.
v.Y = v.Y * f
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := &Vertex{3, 4}
v.Scale(5)
fmt.Println(v, v.Abs())
}
10.2. 인터페이스 (Interface)
: 인터페이스는 메소드의 집합으로 정의 된다. 그 메소들의 구현되어 있는 타입의 값은 모두 인터페이스 타입의 값이 될 수 있다.
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
a = v // a Vertex, does NOT : 포인터 리시버로 변환 되어야지 가능.
// implement Abser
fmt.Println(a.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
10.2.1. 인터페이스 암시적으로 충족
: 타입이 인터페이스의 메소들을 구현하면 인터페이스를 구현한 게 된다. 이를 위해 명시적으로 선언할게 없다. 암시적 인터페이스는 인터페이스를 정의한 패키지로 부터 구현 패키지를 분리(decouple)해 준다. 다른 의존성 또한 없음은 물론이다. 이 특징은 상세하게 인터페이스를 정의하게 독려하고, 모든 구현을 찾아 새 인터페이스 이름으로 태그할 필요가 없기 때문이다. 패키지 io에 Reader 와 Writer가 정의 되어 있다. 따로 정의할 필요가 없다.
package main
import (
"fmt"
"os"
)
type Reader interface {
Read(b []byte) (n int, err error)
}
type Writer interface {
Write(b []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Writer
}
func main() {
var w Writer
// os.Stdout implements Writer
w = os.Stdout
fmt.Fprintf(w, "hello, writer\n")
}