Masih menggunakan code dari modul sebelumnya, pada modul ini kita akan bahas permasalahan pattern singleton.
Skenarionya adalah, Anda ingin mengembalikan jumlah populasi beberapa kota.
Dari kode sebelumnya, kita tambahkan fungsi berikut.
func GetTotalPopulation(cities []string) int {
result := 0
for _, city := range cities {
result += GetSingletonDatabase().GetPopulation(city)
}
return result
}
Kemudian, kita akan membuat test unit untuk memastikan fungsi diatas berjalan benar.
cities := []string{"Seoul", "Jakarta"}
tp := GetTotalPopulation(cities)
ok := tp == (17500000 + 14250000)
fmt.Println(ok)
Dari unit test diatas, terlihat masalah karena kita menggunakan hard code nilai populasi pembanding. Dan test yang dilakukan bukan hanya untuk test apakah fungsi GetTotalPopulation berjalan dengan benar atau tidak, tetapi melakukan test database loading.
Hal ini terjadi karena kita menggunakan pattern singleton, yang pada dasarnya sudah melanggar prinsip dari DIP.
Pada fungsi GetTotalPopulation kita tergantung pada intance GetSingletonDatabase, sementara konsep dari DIP adalah jangan bergantung pada concrete implementation, namun pada abstraction.
Berikut kode lengkapnya.
package main import ( "bufio" "fmt" "os" "strconv" "sync" ) //digunakan huruf kecil s, agar tidak bisa diakses oleh user type singletonDatabase struct { capitals map[string]int } //fungsi untuk mengambil data jumlah populasi func (db *singletonDatabase) GetPopulation(name string) int { return db.capitals[name] } //fungsi untuk membaca file txt func readData(path string) (map[string]int, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) result := map[string]int{} for scanner.Scan() { k := scanner.Text() scanner.Scan() v, _ := strconv.Atoi(scanner.Text()) result[k] = v } return result, nil } // digunakan sync.Once untuk memastikan suatu proses dijalankan 1 kali // sync.Once mempunyai fitur lazy, yaitu hanya akan dipanggil saat dibutuhkan var once sync.Once var instance *singletonDatabase func GetSingletonDatabase() *singletonDatabase { once.Do(func() { caps, e := readData(".\\capitals.txt") db := singletonDatabase{caps} if e == nil { db.capitals = caps } instance = &db }) return instance } func GetTotalPopulation(cities []string) int { result := 0 for _, city := range cities { result += GetSingletonDatabase().GetPopulation(city) } return result } func main() { cities := []string{"Seoul", "Jakarta"} tp := GetTotalPopulation(cities) ok := tp == (17500000 + 14250000) fmt.Println(ok) }
Pada modul berikutnya kita akan bahas penyelesaian masalah diatas.