Table of Contents

02 – Membuat Modul

Membuat Modul

Dalam sebuah modul, kita mengumpulkan satu atau lebih paket untuk sekumpulan fungsi yang diskret dan berguna. Contohnya, kita bisa membuat sebuah modul untuk paket-paket yang memiliki fungsi-fungsi yang melakukan analisis finansial sehingga orang lain yang membuat aplikasi finansial dapat menggunakan karya kita.

Saat kita menambahkan atau memperbaiki fungsionalitas dalam modul, kita menerbitkan versi terbaru dari modul. Pengembang lain yang memanggil fungsi dalam modul kita dapat meng-impor paket-paket yang diperbarui dari modul kita dan mengujinya dengan versi yang terbaru sebelum menggunakan untuk production.

				
					cd
mkdir greetings
cd greetings
go mod init example.com/greetings
				
			

Buat file greetings.go

				
					package greetings

import "fmt"

// Hello mengembalikan sebuah salam untuk nama orang tertentu.
func Hello(name string) string {
	// Kembalikan sebuah salam yang berisi `name` dalam sebuah pesan.
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message
}
				
			

Ini adalah kode pertama Anda dalam modul. Ia berisi sebuah fungsi Hello yang mengembalikan sebuah salam untuk setiap pemanggilan. Kita akan menulis kode yang memanggil fungsi tersebut di langkah selanjutnya.

Memanggil kode dari modul lain

Pada bagian sebelumnya, kita telah membuat sebuah modul “greetings”. Pada bagian ini, kita akan menulis kode, dalam bentuk aplikasi yang bisa dieksekusi, yang memanggil fungsi Hello dalam modul tersebut.

Buat sebuah direktori “hello” untuk sumber kode modul Go. Di direktori ini kita akan menulis kode yang memanggil modul sebelumnya.

Setelah membuat direktori tersebut, Anda seharusnya memiliki dua direktori: “hello” dan “greetings” dengan hirarki yang sama, seperti berikut:

				
					<home>/
 |-- greetings/
 |-- hello/
				
			

Misal anda masih berada di dalam di rektori greetings

				
					cd ..
mkdir hello
cd hello
go mod init example.com/hello
				
			

Buat file baru bernama hello.go

				
					package main

import (
	"fmt"

	"example.com/greetings"
)

func main() {
	// Ambil pesan salaman dari fungsi Hello dan cetak ke layar.
	message := greetings.Hello("Gladys")
	fmt.Println(message)
}
				
			

Umumnya penggunaan dalam lingkungan production, Anda sebaiknya menerbitkan modul “example.com/greetings” pada sebuah repositori (dengan path modul yang merefleksikan lokasi diterbitkan), yang mana perkakas Go dapat temukan untuk mengunduhnya. Untuk sekarang, karena kita belum menerbitkan modul, kita harus mengadaptasi modul “example.com/hello” supaya dapat menemukan kode “example.com/greetings” dalam sistem berkas lokal Anda.

Sunting modul “example.com/hello” supaya menggunakan modul “example.com/greetings” yang ada di lokal Anda.

				
					go mod edit --replace=example.com/greetings=../greetings
				
			

Perintah tersebut memberitahu supaya mengganti “example.com/greetings” dengan “../greetings” saat mencari lokasi dependensi. Setelah perintah dijalankan, berkas “go.mod” dalam direktori “hello” akan berisi sebuah direktif “replace”:

				
					module example.com/hello

go 1.16

replace example.com/greetings => ../greetings
				
			

Lalu aktifkan dependency dengan perintah berikut :

				
					go mod tidy
				
			
				
					go: found example.com/greetings in example.com/greetings v0.0.0-00010101000000-000000000000
				
			

Setelah perintah selesai, berkas “go.mod” dalam modul “example.com/hello” akan berisi seperti berikut:

				
					module example.com/hello

go 1.16

replace example.com/greetings => ../greetings

require example.com/greetings v0.0.0-00010101000000-000000000000
				
			

Perintah tersebut berfungsi untuk menemukan modul lokal dalam direktori “greetings”, kemudian menambahkan direktif require untuk menspesifikasikan bahwa “example.com/hello” membutuhkan “example.com/greetings”. Kita memakai dependensi ini saat kita mengimpor paket “greetings” dalam “hello.go”.

Angka setelah path modul adalah versi-palsu sebuah angka yang dibangkitkan sebagai pengganti dari angka versi semantik (yang belum gunakan oleh modul “greetings”).

Untuk mengarahkan ke modul yang telah diterbitkan, sebuah berkas go.mod tidak menggunakan direktif “replace” tapi hanya menggunkan direktif “require” dengan angka versi dibagian belakangnya.

				
					require example.com/greetings v1.1.0
				
			

Jalankan kode

				
					go run .
				
			
				
					Hi, Gladys. Welcome!
				
			

Berikut cara membuat 2 modul dan memanggil salah satunya.

Mengembalikan dan Menangani Error

Penanganan eror adalah fitur penting pada kode yang solid. Pada bagian ini, kita akan menambahkan sedikit kode yang mengembalikan sebuah eror pada modul “greetings”, kemudian menangani-nya pada sisi pemanggil (modul “hello”).

Misal kembalikan error jika parameter name kosong, Ubah kode berikut pada greetings.go :

				
					package greetings

import (
	"errors"
	"fmt"
)

// Hello mengembalikan sebuah salaman untuk nama seseorang.
func Hello(name string) (string, error) {
	// Jika name kosong, kembalikan sebuah eror dengan pesan tertentu.
	if name == "" {
		return "", errors.New("empty name")
	}

	// Jika nama tidak kosong, kembalikan sebuah pesan salam yang menanam
	// nama tersebut.
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message, nil
}
				
			

Berkut penjelasan baris kode di atas

  • Mengimpor paket errors dari pustaka standar supaya dapat menggunakan fungsi errors.New.
  • Mengubah fungsi Hello sehingga mengembalikan dua nilai: sebuah string dan sebuah error. Yang memanggil fungsi tersebut akan memeriksa nilai kembalian kedua untuk mengetahui jika ada eror yang terjadi.
  • Menambahkan perintah if untuk memeriksa pemanggilan yang tidak valid (pada kasus ini yaitu nama yang kosong) dan mengembalikan sebuah eror jika pemanggilan tidak valid. Fungsi errors.New mengembalikan sebuah eror dengan pesan di dalamnya.
  • Menambahkan nil (artinya tidak ada eror) sebagai nilai kedua pada saat kembalian sukses. Dengan cara ini, pemanggil fungsi dapat memeriksa bila mana fungsi sukses.

Lalu buka file “hello/hello.go”, tangani kembalian eror dari fungsi Hello, berikut juga nilai yang tidak eror.

Salin kode berikut ke “hello.go”

				
					package main

import (
	"fmt"
	"log"

	"example.com/greetings"
)

func main() {
	// Set properti dari Logger, termasuk prefiks dan flag untuk
	// menon-aktifkan pencetakan waktu, sumber berkas, dan nomor baris.
	log.SetPrefix("greetings: ")
	log.SetFlags(0)

	// Ambil pesan salam.
	message, err := greetings.Hello("")

	// Jika ada eror, cetak ke layar dan keluar dari program.
	if err != nil {
		log.Fatal(err)
	}

	// Jika tidak ada eror, cetak pesan yang dikembalikan ke layar.
	fmt.Println(message)
}
				
			

Penjelasan :

  • Mengonfigurasi paket log untuk mencetak nama perintah (“greetings: “) di awal pesan log, tanpa penanda waktu atau informasi sumber berkas.
  • Menyimpan nilai kembalian dari Hello, termasuk eror, ke variabel.
  • Mengubah argumen ke fungsi Hello dari string “Glady” ke string kosong, supaya kita dapat mencoba kode pas penanganan eror.
  • Memeriksa nilai eror yang tidak nil. Jika eror, stop program.
  • Gunakan fungsi dari paket log untuk menampilkan informasi eror. Jika ada error, gunakan fungsi Fatal pada paket log untuk mencetak eror dan menghentikan program.

Pada terminal, di dalam direktori “hello”, jalankan “hello.go” untuk memeriksa apakah kode berjalan dengan benar atau tidak.

Sekarang, karena kita mengirim nama yang kosong, kita akan mendapatkan sebuah eror.

				
					go run .
				
			
				
					greetings: empty name
exit status 1
				
			

Contoh penanganan error tersebut sangat umum dalam Go: Mengembalikan sebuah error sebagai nilai supaya pemanggil dapat memeriksanya.

Mengembalikan Kalimat dengan Format Acak

Pada bagian ini, kita akan mengubah kode supaya tidak hanya mengembalikan satu bentuk salam saja, tetapi mengembalikan satu dari beberapa pesan salam yang telah ditentukan.

Untuk melakukan hal ini, kita akan menggunakan sebuah slice. Sebuah slice yaitu seperti sebuah array, namun ukurannya dapat berubah secara dinamis saat kita menambah atau menghapus item dalam slice. Slice adalah salah satu dari tipe yang sangat berguna pada Go.

Kita akan membuat slice berukuran kecil yang berisi tiga pesan salam, kemudian kita kembalikan salah satu dari pesan tersebut secara acak. Untuk informasi lebih lanjut tentang slice, lihat Go slice.

Dalam “greetings/greetings.go”, ubah kode kita supaya seperti di bawah.

				
					package greetings

import (
	"errors"
	"fmt"
	"math/rand"
	"time"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
	// If no name was given, return an error with a message.
	if name == "" {
		return name, errors.New("empty name")
	}
	// Create a message using a random format.
	message := fmt.Sprintf(randomFormat(), name)
	return message, nil
}

// init sets initial values for variables used in the function.
func init() {
	rand.Seed(time.Now().UnixNano())
}

// randomFormat returns one of a set of greeting messages. The returned
// message is selected at random.
func randomFormat() string {
	// A slice of message formats.
	formats := []string{
		"Hi, %v. Welcome!",
		"Great to see you, %v!",
		"Hail, %v! Well met!",
	}

	// Return a randomly selected message format by specifying
	// a random index for the slice of formats.
	return formats[rand.Intn(len(formats))]
}
				
			

Penjelasan :

  • Menambahkan fungsi randomFormat yang mengembalikan format untuk pesan salam secara acak. Ingatlah bahwa randomFormat diawali dengan huruf kecil, membuatnya hanya dapat diakses oleh kode di dalam paketnya sendiri (dengan kata lain, tidak dieskpor).
  • Dalam randomFormat, kita mengisi sebuah slice string dengan tiga format pesan. Saat mendeklarasikan sebuah slice, kita tidak mengisi ukuran dalam tanda kurung siku, seperti ini: []string. Perintah ini mengatakan ukuran dari array di balik slice dapat diubah secara dinamis.
  • Menggunakan paket math/rand untuk membangkitkan angka acak untuk memilih sebuah item di dalam slice.
  • Menambahkan fungsi init untuk menanam paket rand dengan waktu sekarang. Go mengeksekusi fungsi init secara otomatis pada saat program berjalan, setelah semua global variabel diinisiasi. Untuk informasi lebih lanjut tentang fungsi init, lihat Efektif Go.
  • Dalam fungsi Hello, panggil fungsi randomFormat untuk mendapatkan format dari pesan yang akan dikembalikan, kemudian gunakan format tersebut beserta value dari name untuk membuat pesan.
  • Kembalikan pesan tersebut (atau sebuah eror) seperti sebelumnya.

Dalam “hello/hello.go”, ubah kode kita supaya seperti di bawah.

Kirim string “Glady” (atau nama apa pun yang Anda sukai) sebagai argumen dari fungsi Hello dalam hello.go.

				
					package main

import (
	"fmt"
	"log"

	"example.com/greetings"
)

func main() {
	// Set properti dari Logger, termasuk prefiks dan flag untuk
	// menon-aktifkan pencetakan waktu, sumber berkas, dan nomor baris.
	log.SetPrefix("greetings: ")
	log.SetFlags(0)

	// Ambil pesan salam.
	message, err := greetings.Hello("Gladys")

	// Jika ada eror, cetak ke layar dan keluar dari program.
	if err != nil {
		log.Fatal(err)
	}

	// Jika tidak ada eror, cetak pesan yang dikembalikan ke layar.
	fmt.Println(message)
}
				
			

Lalu jalankan kode beberapa kali, hasilnya akan berubah-ubah seperti di bawah ini :

				
					$ go run .
Great to see you, Gladys!

$ go run .
Hi, Gladys. Welcome!

$ go run .
Hail, Gladys! Well met!
				
			
Megembalikan Kalimat untuk beberapa Orang(Array)

Pada bagian ini, kita akan menambah fungsionalitas pada modul “greetings” supaya dapat mengembalikan pesan salam untuk beberapa orang dalam satu pemanggilan. Dengan kata lain, kita akan menangani input dengan banyak nilai, kemudian memasangkan nilai input tersebut dengan nilai output. Untuk melakukan hal ini, kita akan mengirim sekumpulan nama ke sebuah fungsi yang dapat mengembalikan salam untuk setiap nama tersebut.

Mengubah parameter fungsi Hello dari satu nama menjadi beberapa nama akan mengubah signature dari fungsi tersebut. Jika Anda telah menerbitkan modul “example.com/greetings” sebelumnya dan user telah menulis kode yang telah memanggil Hello, perubahan tersebut akan membuat program mereka gagal dikompilasi.

Dalam situasi ini, pilihan yang terbaik yaitu membuat sebuah fungsi baru dengan nama yang berbeda. Fungsi yang baru tersebut akan menerima satu atau lebih nilai. Dengan ini kita menjaga fungsi yang lama demi menjaga kompatibilitas.

Dalam “greetings/greetings.go”, ubah lah kode Anda supaya seperti berikut :

				
					package greetings

import (
	"errors"
	"fmt"
	"math/rand"
	"time"
)

// Hello mengembalikan sebuah salam untuk nama seseorang.
func Hello(name string) (string, error) {
	// Jika nama kosong, kembalikan sebuah eror dengan pesan.
	if name == "" {
		return name, errors.New("empty name")
	}
	// Buat sebuah pesan salam dengan format acak.
	message := fmt.Sprintf(randomFormat(), name)
	return message, nil
}

// Hellos mengembalikan sebuah map yang mengasosiasikan setiap nama orang
// dengan sebuah pesan salam.
func Hellos(names []string) (map[string]string, error) {
	// Sebuah map yang memetakan nama dengan pesan.
	messages := make(map[string]string)

	// Iterasi slice "names", panggil fungsi Hello untuk mendapatkan
	// sebuah pesan untuk setiap nama.
	for _, name := range names {
		message, err := Hello(name)
		if err != nil {
			return nil, err
		}
		// Di dalam map, asosiasikan nama (kunci) dengan pesan (nilai).
		messages[name] = message
	}
	return messages, nil
}

// init men-set pengacak angka.
func init() {
	rand.Seed(time.Now().UnixNano())
}

// randomFormat mengembalikan sekumpulan pesan acak.  Pesan yang dikembalikan
// dipilih secara acak.
func randomFormat() string {
	// Slice dari sekumpulan format pesan.
	formats := []string{
		"Hi, %v. Welcome!",
		"Great to see you, %v!",
		"Hail, %v! Well met!",
	}

	// Kembalikan salah satu format pesan secara acak.
	return formats[rand.Intn(len(formats))]
}

				
			

Penjelasan :

  • Menambah fungsi Hellos dengan parameter sebuah slice string bukan sebuah nama. Kita juga mengubah tipe kembalian dari sebuah string menjadi sebuah map supaya kita dapat mengasosiasikan parameter nama dengan pesan salam.
  • Membuat fungsi Hellos yang memanggil fungsi Hello yang sudah ada. Hal ini cukup membantu mengurangi duplikasi kode.
  • Membuat sebuah map ucapan salam yang mengasosiasikan setiap nama yang diterima (sebagai kunci) dengan pesan yang dibangkitkan (sebagai sebuah nilai). Dalam Go, kita menginisiasi map dengan sintaksis berikut: make(map[tipe-key]tipe-nilai). Kita membuat fungsi Hellos untuk mengembalikan map tersebut ke pemanggil. Untuk informasi lebih lanjut tentang map lihat Cara kerja map pada Go.
  • Melakukan pengulangan terhadap daftar nama yang diterima fungsi Hellos, memeriksa apakah nilainya tidak kosong, kemudian mengasosiasikan setiap pesan dengan nama yang dikirim. Dalam pengulangan ini, perintah range mengembalikan dua nilai: indeks dari item dan salinan dari nilai item. Karena kita tidak membutuhkan indeks, maka digunakan identifikasi kosong (‘_’) untuk mengindahkannya. Untuk informasi lebih lanjut tentang identifikasi kosong, lihat pengidentifikasi kosong dalam Efektif Go.

Dalam “hello.go”, ubah kode Anda menjadi seperti berikut :

				
					package main

import (
	"fmt"
	"log"

	"example.com/greetings"
)

func main() {
	// Set properti dari Logger, termasuk prefiks dan opsi untuk mematikan
	// pencetakan waktu, sumber berkas, dan nomor baris.
	log.SetPrefix("greetings: ")
	log.SetFlags(0)

	// Sebuah slice yang berisi nama-nama.
	names := []string{"Gladys", "Samantha", "Darrin"}

	// Panggil fungsi Hellos untuk mendapatkan pesan salam untuk setiap nama.
	messages, err := greetings.Hellos(names)
	if err != nil {
		log.Fatal(err)
	}
	// Jika tidak ada eror, cetak map yang diterima dari ke layar.
	fmt.Println(messages)
}
				
			

Dalam perubahan ini, kita:

  • Membuat variabel names bertipe slice yang menyimpan tiga nama.
  • Mengirim variabel names sebagai argumen ke fungsi Hellos.

Pada terminal, pindah lah ke direktori yang menyimpan “hello/hello.go”, kemudian jalankan “go run” untuk memastikan kode bekerja dengan benar.

Keluaran dari perintah tersebut harusnya representasi dari nama dan pesan salam, kurang lebih seperti berikut:

				
					go run .
map[Darrin:Hail, Darrin! Well met! Gladys:Hi, Gladys. Welcome! Samantha:Hail, Samantha! Well met!]
				
			

Pelajaran yang dapat diambil:

  • Memperkenalkan map yang merepresentasikan pasangan kunci dan nilai.
  • Memperkenalkan ide tentang menjaga kompatibilitas dengan mengimplementasikan sebuah fungsi baru untuk fungsionalitas yang baru atau yang berubah dalam sebuah modul. Untuk informasi lebih lanjut tentang menjaga kompatibilitas, lihatlah Menjaga modul tetap kompatibel.
Membuat sebuah Test(Unit Test)

Sekarang setelah kode kita menjadi stabil, tambahkan sebuah Test. Menguji kode Anda selama pengembangan dapat menangkap bug yang mungkin terjadi saat perubahan dilakukan. Dalam topik ini, kita akan menambahkan sebuah Test untuk fungsi Hello.

Dukungan bawaan Go untuk unit tes membuat pengembang mudah membuat dan melakukan tes. Khususnya, dengan konvensi penamaan, paket “testing”, dan perintah “go test”, kita dapat dengan cepat menulis dan mengeksekusi tes.

Dalam direktori “greetings”, buatlah sebuah berkas bernama “greetings_test.go”

Dengan mengakhiri sebuah nama berkas dengan “_test.go” berarti memberitahu perintah “go test” bahwa berkas tersebut berisi fungsi-fungsi tes.

Dalam “greetings_test.go”, salin lah kode berikut ke dalam berkas dan simpan.

				
					console.log( 'Code is Poetry' );