Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
03 Apr 2020 · 6 mnt baca ·Artikel 6 / 119
Go

Mengenal Struct dan Interface Pada Golang

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

Meskipun mungkin bagi kita untuk menulis program hanya menggunakan tipe data bawaan Go, pada titik tertentu itu akan menjadi sangat membosankan. Pertimbangkan program yang berinteraksi dengan bentuk seperti kode dibawah ini.

go
 1package main
 2
 3import (
 4	"fmt"
 5	"math"
 6)
 7
 8func distance(x1, y1, x2, y2 float64) float64 {
 9	a := x2 - x1
10	b := y2 - y1
11	return math.Sqrt(a*a + b*b)
12}
13
14func rectangleArea(x1, y1, x2, y2 float64) float64 {
15	l := distance(x1, y1, x1, y2)
16	w := distance(x1, y1, x2, y1)
17	return l * w
18}
19
20func circleArea(x, y, r float64) float64 {
21	return math.Pi * r * r
22}
23
24func main() {
25	var rx1, ry1 float64 = 0, 0
26	var rx2, ry2 float64 = 10, 10
27	var cx, cy, cr float64 = 0, 0, 5
28	fmt.Println(rectangleArea(rx1, ry1, rx2, ry2))
29	fmt.Println(circleArea(cx, cy, cr))
30}

Melacak semua koordinat membuat sulit untuk melihat apa yang sedang dilakukan program dan kemungkinan akan menyebabkan kesalahan.

Structs

Cara mudah untuk membuat program ini agar lebih baik adalah dengan menggunakan struct. Sebuah struct adalah tipe yang berisi bidang bernama. Misalnya kita bisa mewakili lingkaran seperti ini:

go
1type Circle struct {
2  x float64
3  y float64
4  r float64 
5}
Kata kunci type memperkenalkan tipe baru. Diikuti dengan nama tipe (Lingkaran), kata kunci struct untuk menunjukkan bahwa kita mendefinisikan tipe struct dan daftar bidang di dalam kurung kurawal. Setiap bidang memiliki nama dan jenis. Seperti halnya fungsi, kita dapat menutup bidang yang memiliki tipe yang sama:
go
1type Circle struct {
2     x, y, r float64
3}

Inisialiasi

Kita dapat membuat turunan dari tipe Lingkaran baru kita dalam berbagai cara:

go
1  var c Circle

Seperti tipe data lainnya, ini akan membuat variabel Cirlce lokal yang secara default diset ke nol. Untuk struct nol berarti masing-masing bidang diatur ke nilai nol yang sesuai (0 untuk int, 0,0 untuk float, "" untuk string, nil untuk pointer, …) Kita juga dapat menggunakan fungsi new:

go
1  c := new(Circle)
Ini mengalokasikan memori untuk semua bidang, menetapkan masing-masing ke nilai nol dan mengembalikan pointer. (*Circle) Lebih sering kita gunakan untuk memberi nilai pada setiap field. Kita bisa melakukan ini dengan dua cara. Seperti ini:
go
1  c := Circle{x: 0, y: 0, r: 5}
Atau kita dapat mengabaikan nama field jika kita mengetahui urutannya:
go
1  c := Circle{0, 0, 5}

Fields

Kita dapat mengakses field menggunakan . operator:

go
1  fmt.Println(c.x, c.y, c.r)
2  c.x = 10
3  c.y = 5

Mari kita ubah fungsi circleArea sehingga menggunakan Circle:

go
1func circleArea(c Circle) float64 {
2     return math.Pi * c.r*c.r
3}
lalu di dalam fungsi main kita berikan:
go
1  c := Circle{0, 0, 5}
2  fmt.Println(circleArea(c))

Satu hal yang perlu diingat adalah bahwa argumen selalu disalin di Go. Jika kita mencoba untuk mengubah salah satu field di dalam fungsi circleArea, itu tidak akan mengubah variabel asli. Karena itu, kita biasanya menulis fungsi seperti ini:

go
1func circleArea(c *Circle) float64 {
2     return math.Pi * c.r*c.r
3}
Dan ubah pada fungsi main menjadi:
go
1  c := Circle{0, 0, 5}
2  fmt.Println(circleArea(&c))

Methods

Meskipun ini lebih baik daripada versi pertama dari kode ini, kita dapat mengubahnya dengan menggunakan jenis fungsi khusus yang dikenal sebagai method:

go
1func (c *Circle) area() float64 {
2     return math.Pi * c.r*c.r
3}
Di antara kata kunci func dan nama fungsi, kita telah menambahkan receiver. receiver seperti parameter ia memiliki nama dan tipe tetapi dengan membuat fungsi dengan cara ini, kita dapat memanggil fungsi menggunakan . operator:
go
1  fmt.Println(c.area())
Ini jauh lebih mudah dibaca, kita tidak lagi membutuhkan operator & (Go secara otomatis tahu untuk melewatkan pointer ke lingkaran untuk metode ini) dan karena fungsi ini hanya dapat digunakan dengan circle, kita dapat mengganti nama fungsi menjadi area saja.

Mari kita lakukan hal yang sama untuk persegi panjang:

go
1type Rectangle struct {
2     x1, y1, x2, y2 float64
3}
4func (r *Rectangle) area() float64 {
5     l := distance(r.x1, r.y1, r.x1, r.y2)
6     w := distance(r.x1, r.y1, r.x2, r.y1)
7     return l * w
8}
dan pada main kita memiliki:
go
1r := Rectangle{0, 0, 10, 10}
2fmt.Println(r.area())

Embedded Types

Field struct biasanya mewakili saling keterikatan. Misalnya Circle memiliki radius. Misalkan kita memiliki orang struct:

go
1type Person struct {
2    Name string
3}
4func (p *Person) Talk() {
5    fmt.Println("Hi, my name is", p.Name)
6}
Dan kita ingin membuat struct Android baru. Kita bisa melakukan ini:
go
1type Android struct {
2    Person Person
3    Model string
4}

Go mendukung keterikatan struct seperti ini dengan menggunakan tipe yang disematkan. Juga dikenal sebagai bidang anonim, jenis yang disematkan terlihat seperti ini:

go
1type Android struct {
2    Person
3    Model string
4}
Kita menggunakan tipe (Person) dan tidak memberinya nama. Ketika didefinisikan dengan cara ini, struct Person dapat diakses menggunakan nama tipe:
go
1  a := new(Android)
2  a.Person.Talk()

Tetapi kita juga dapat memanggil metode Person apa pun secara langsung di Android:

go
1a := new(Android)
2a.Talk()

Interfaces

Baik dalam kehidupan nyata maupun dalam pemrograman, hubungan seperti ini adalah hal yang lumrah. Go memiliki cara untuk membuat kesamaan yang tidak disengaja ini menjadi eksplisit melalui tipe yang dikenal sebagai Interface. Berikut adalah contoh interface Shape:

go
1type Shape interface {
2  area() float64
3}

Seperti sebuah struct, interface dibuat menggunakan type, diikuti dengan nama dan interface. Tapi untuk mendefinisikan Shape, kita membutuhkan "method set".Kumpulan method adalah daftar method yang harus dimiliki suatu tipe untuk “mengimplementasikan” interface.

go
1func totalArea(shapes ...Shape) float64 {
2	var area float64
3	for _, s := range shapes {
4		area += s.area()
5	}
6	return area
7}
kita akan memanggil fungsi ini seperti ini:
go
1  fmt.Println(totalArea(&c, &r))

Interface juga dapat digunakan sebagai fields:

go
1type MultiShape struct {
2     shapes []Shape
3}
Kita bahkan dapat mengubah MultiShape menjadi Shape dengan memberinya metode area:
go
1func (m *MultiShape) area() float64 {
2	var area float64
3	for _, s := range m.shapes {
4		area += s.area()
5	}
6	return area
7}
Sekarang MultiShape dapat berisi Circles, Rectangles atau bahkan MultiShapes lainnya.

Berikut ini program lebih lengkapnya

go
 1package main
 2
 3import (
 4	"fmt"
 5	"math"
 6)
 7
 8type Circle struct {
 9	x, y, r float64
10}
11
12func (c *Circle) area() float64 {
13	return math.Pi * c.r * c.r
14}
15
16type Rectangle struct {
17	l, w float64
18}
19
20func (r *Rectangle) area() float64 {
21	return r.l * r.w
22}
23
24type Shape interface {
25	area() float64
26}
27
28type MultiShape struct {
29	shapes []Shape
30}
31
32func (m *MultiShape) area() float64 {
33	var area float64
34	for _, s := range m.shapes {
35		area += s.area()
36	}
37	return area
38}
39
40func totalArea(shapes ...Shape) float64 {
41	var area float64
42	for _, s := range shapes {
43		area += s.area()
44	}
45	return area
46}
47
48func main() {
49	c := Circle{0, 0, 5}
50	r := Rectangle{3, 4}
51	fmt.Println(totalArea(&c, &r))
52
53	m := MultiShape{}
54	m.shapes = append(m.shapes, &c, &r)
55	fmt.Println(m.area())
56}

Artikel Terkait

💬 Komentar