Pola Opsi Fungsional
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan GO di Coddy. Pelajaran 79 dari 107.
Saat membuat struct dengan banyak field konfigurasi opsional, fungsi konstruktor bisa menjadi sulit dikelola. Pola Opsi Fungsional mengatasi masalah ini dengan elegan menggunakan fungsi untuk mengonfigurasi object.
Pertimbangkan struct server dengan beberapa pengaturan opsional. Alih-alih menggunakan konstruktor dengan banyak parameter, kita mendefinisikan tipe opsi dan fungsi yang mengembalikan opsi:
type Server struct {
host string
port int
timeout int
}
type Option func(*Server)
func WithPort(port int) Option {
return func(s *Server) {
s.port = port
}
}
func WithTimeout(timeout int) Option {
return func(s *Server) {
s.timeout = timeout
}
}Konstruktor menerima list opsi variadik dan menerapkan setiap opsi:
func NewServer(host string, opts ...Option) *Server {
s := &Server{
host: host,
port: 8080, // bawaan
timeout: 30, // bawaan
}
for _, opt := range opts {
opt(s)
}
return s
}Sekarang, membuat server menjadi lebih mudah dibaca dan fleksibel:
// Gunakan pengaturan bawaan
s1 := NewServer("localhost")
// Sesuaikan opsi tertentu
s2 := NewServer("localhost", WithPort(3000), WithTimeout(60))Pola ini unggul karena pemanggil hanya menentukan yang mereka butuhkan, nilai default terlihat jelas di satu tempat, dan penambahan opsi baru tidak merusak kode yang sudah ada. Pola ini banyak digunakan di library Go seperti grpc dan zap.
Tantangan
MudahMari buat koneksi database yang dapat dikonfigurasi dengan Functional Options Pattern! Pola ini sangat cocok saat kamu memiliki banyak pengaturan opsional dengan nilai default yang masuk akal, persis seperti yang dibutuhkan koneksi database.
Kamu akan mengatur kodemu dalam dua file:
database.go: Definisikan tipe koneksi database dan fungsi opsi.Buat struct
DBConnectiondengan field yang tidak diekspor berikut:host(string)port(int)username(string)password(string)maxConnections(int)timeout(int), dalam detik
Definisikan tipe
Optionsebagai fungsi yang memodifikasi*DBConnection.Buat fungsi opsi berikut yang mengembalikan
Option:WithPort(port int)WithCredentials(username, password string)WithMaxConnections(max int)WithTimeout(seconds int)
Buat constructor
NewDBConnection(host string, opts ...Option)yang mengembalikan*DBConnection. Tetapkan nilai default berikut: port5432, username"admin", password"secret", maxConnections10, timeout30. Terapkan semua opsi yang diberikan setelah menetapkan nilai default.Tambahkan method
ConnectionString()yang mengembalikan info koneksi dalam format ini:[username]:[password]@[host]:[port] (max:[maxConnections], timeout:[timeout]s)main.go: Buat koneksi database dengan berbagai konfigurasi.Baca host, lalu baca jumlah opsi yang akan diterapkan. Untuk setiap opsi, baca tipe opsi dan nilainya:
portdiikuti nomor portcredentialsdiikuti username dan password (dua baris)maxconndiikuti jumlah koneksi maksimumtimeoutdiikuti batas waktu dalam detik
Buat
DBConnectiondengan opsi yang ditentukan dan cetak string koneksinya.
Input berikut akan diberikan:
- Baris 1: Nama host
- Baris 2: Jumlah opsi
- Baris berikutnya: Tipe opsi dan nilainya
Contohnya, jika diberikan:
localhost
0Output kamu seharusnya:
admin:secret@localhost:5432 (max:10, timeout:30s)Dan jika diberikan:
db.example.com
2
port
3306
credentials
root
mypasswordOutput kamu seharusnya:
root:mypassword@db.example.com:3306 (max:10, timeout:30s)Dan jika diberikan:
production.server
4
port
5433
credentials
dbuser
securepass123
maxconn
50
timeout
60Output kamu seharusnya:
dbuser:securepass123@production.server:5433 (max:50, timeout:60s)Perhatikan bahwa pemanggil hanya menentukan opsi yang dibutuhkan: pengaturan yang tidak ditentukan akan menggunakan nilai default yang masuk akal. Inilah keunggulan Functional Options Pattern!
Coba sendiri
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Baca host
host, _ := reader.ReadString('\n')
host = strings.TrimSpace(host)
// Baca jumlah opsi
numOptionsStr, _ := reader.ReadString('\n')
numOptions, _ := strconv.Atoi(strings.TrimSpace(numOptionsStr))
// Kumpulkan opsi
var opts []Option
for i := 0; i < numOptions; i++ {
optionType, _ := reader.ReadString('\n')
optionType = strings.TrimSpace(optionType)
switch optionType {
case "port":
portStr, _ := reader.ReadString('\n')
port, _ := strconv.Atoi(strings.TrimSpace(portStr))
// TODO: Tambahkan opsi WithPort ke opts
_ = port // Hapus baris ini saat kamu mengimplementasikannya
case "credentials":
username, _ := reader.ReadString('\n')
username = strings.TrimSpace(username)
password, _ := reader.ReadString('\n')
password = strings.TrimSpace(password)
// TODO: Tambahkan opsi WithCredentials ke opts
_, _ = username, password // Hapus baris ini saat kamu mengimplementasikannya
case "maxconn":
maxStr, _ := reader.ReadString('\n')
maxConn, _ := strconv.Atoi(strings.TrimSpace(maxStr))
// TODO: Tambahkan opsi WithMaxConnections ke opts
_ = maxConn // Hapus baris ini saat kamu mengimplementasikannya
case "timeout":
timeoutStr, _ := reader.ReadString('\n')
timeout, _ := strconv.Atoi(strings.TrimSpace(timeoutStr))
// TODO: Tambahkan opsi WithTimeout ke opts
_ = timeout // Hapus baris ini saat kamu mengimplementasikannya
}
}
// TODO: Buat DBConnection menggunakan NewDBConnection dengan host dan opts
// TODO: Cetak string koneksi menggunakan method ConnectionString()
_ = opts // Hapus baris ini saat kamu mengimplementasikannya
fmt.Println("TODO: Print connection string here")
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Objek
1Dasar-Dasar OOP di Go
File EksternalWorkspace Go dan modulePaket dan ImporNama yang Diekspor dan Tidak DieksporPengantar OOP di Gostruct sebagai classMendefinisikan method pada structPointer receiver vs value receiverInisialisasi structFungsi KonstruktorUlasan: Kalkulator Sederhana4Interface
Pengenalan InterfaceImplementasi ImplisitInterface sebagai KontrakInterface Kosong (any)Type AssertionType SwitchKomposisi InterfaceInterface Stringer & ErrorUlasan: Kalkulator Bentuk7Enkapsulasi
Field yang Diekspor vs Tidak DieksporEnkapsulasi Tingkat PackageMethod Getter dan SetterPenyembunyian Informasi di GoRingkasan: Data Siswa10Generics (Go 1.18+)
Pengantar GenericsParameter TipeBatasan TipeStruct GenerikCara Alternatif untuk Method GenerikRingkasan Koleksi Generik2Mendalami Tipe dan Struct
Tipe Dasar dan KompositDefinisi Tipe KustomTag StructStruct AnonimStruct BersarangNilai Nol dan Nilai BawaanRangkuman: Buku Kontak5Komposisi daripada Pewarisan
Mengapa Go Tidak Memiliki PewarisanDasar Penyematan StructPromosi methodMenyematkan Beberapa StructPenyematan vs AgregasiMembayangi method yang DisematkanRangkuman: Hierarki Karyawan8Penanganan Error & OOP
Interface errorTipe Error KustomPembungkusan Error (fmt.Errorf)Error Sentinelerrors.Is() dan errors.As()Panic, Defer, dan RecoverRangkuman: Parser File3Pointer dan Memori
Dasar-Dasar Pointer di GoPointer ke StructPass by Value vs ReferenceFungsi new()Garbage Collection di GoRekap: Pembuat Linked List6Polimorfisme di Go
Polimorfisme melalui InterfaceDuck Typing di GoAturan Pemenuhan InterfaceKoleksi PolimorfikInjeksi DependensiRangkuman: Pemroses Pembayaran9Konkruensi & OOP
Dasar-dasar GoroutineChannel & KomunikasiChannel Ber-buffer vs Tanpa BufferPernyataan selectsync.Mutex & sync.RWMutexsync.WaitGroupDesain Struct yang Aman untuk ThreadRingkasan: Worker Pool12Konsep OOP Lanjutan
Pola Opsi FungsionalPola Builder di GoMethod ChainingAlias Tipe vs DefinisiDasar-Dasar ReflectionGambaran Umum Pembuatan KodeLatihan sendiri: Compiler Go online