Pola Observer
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan GO di Coddy. Pelajaran 89 dari 107.
Observer pattern mendefinisikan hubungan satu-ke-banyak antar-object. Saat satu object (subjek) berubah keadaan, semua dependennya (observer) akan diberi tahu secara otomatis. Pola ini ideal untuk sistem event, notifikasi, atau skenario apa pun yang mengharuskan beberapa komponen bereaksi terhadap perubahan.
Di Go, kita menerapkan pola ini menggunakan interface untuk observer dan struct subject yang mengelola langganan:
type Observer interface {
Update(event string)
}
type Subject struct {
observers []Observer
}
func (s *Subject) Subscribe(o Observer) {
s.observers = append(s.observers, o)
}
func (s *Subject) Notify(event string) {
for _, observer := range s.observers {
observer.Update(event)
}
}Tipe apa pun yang mengimplementasikan interface Observer dapat berlangganan untuk menerima notifikasi:
type EmailAlert struct{ Address string }
func (e EmailAlert) Update(event string) {
fmt.Printf("Email to %s: %s\n", e.Address, event)
}
type Logger struct{}
func (l Logger) Update(event string) {
fmt.Println("Log:", event)
}Subjek memberi tahu semua pengamat tanpa mengetahui tipe konkretnya:
subject := &Subject{}
subject.Subscribe(EmailAlert{Address: "user@example.com"})
subject.Subscribe(Logger{})
subject.Notify("Order placed")
// Email ke user@example.com: Pesanan dibuat
// Log: Pesanan dibuatPola ini memisahkan subject dari para observer-nya, sehingga kamu dapat dengan mudah menambahkan jenis observer baru tanpa mengubah kode yang sudah ada. Pola ini umum digunakan dalam arsitektur berbasis peristiwa, framework GUI, dan sistem pesan pub/sub.
Tantangan
MudahMari buat sistem peringatan harga saham menggunakan pola Observer! Kamu akan membuat sistem tempat beberapa layanan peringatan secara otomatis menerima notifikasi setiap kali harga saham berubah. Ini cocok untuk menunjukkan cara observer dapat merespons peristiwa tanpa mengetahui tipe konkret subject.
Kamu akan mengatur kodemu dalam tiga file:
observer.go: Definisikan interface observer dan tipe observer konkret.Buat interface
Observerdengan methodUpdate(stockName string, price float64)yang menerima informasi saham saat harga berubah.Implementasikan dua tipe observer:
PriceAlertdengan fieldThreshold(float64), saat diperbarui, mengembalikanALERT: [stockName] at $[price] crossed threshold $[threshold]jika harga berada di atas ambang batas, atauWATCHING: [stockName] at $[price] (threshold: $[threshold])jika berada di bawahPriceLoggerdengan fieldLogName(string), saat diperbarui, mengembalikan[logName] recorded [stockName]: $[price]
Kedua observer harus memiliki method yang mengembalikan string respons berformat.
stock.go: Buat subject yang mengelola observer dan memberi tahu mereka tentang perubahan harga.Buat struct
Stockdengan field untukName(string),Price(float64), dan slice observer. Tambahkan method berikut:Subscribe(o Observer)untuk menambahkan observerSetPrice(price float64) []stringyang memperbarui harga dan memberi tahu semua observer, lalu mengembalikan slice berisi semua respons mereka
Buat juga konstruktor
NewStock(name string, initialPrice float64) *Stock.main.go: Siapkan saham beserta observer-nya, lalu simulasikan perubahan harga.Baca nama saham dan harga awal. Lalu baca jumlah observer yang akan dibuat. Untuk setiap observer, baca tipenya (
alertataulogger) dan konfigurasinya (ambang batas untuk peringatan, nama log untuk logger). Langgankan setiap observer ke saham.Terakhir, baca harga baru, perbarui saham, lalu cetak respons setiap observer pada barisnya sendiri.
Input berikut akan diberikan:
- Baris 1: Nama saham
- Baris 2: Harga awal
- Baris 3: Jumlah observer
- Untuk setiap observer: tipe (
alertataulogger), lalu nilai konfigurasi - Baris terakhir: Harga baru yang akan ditetapkan
Format semua harga dengan dua angka desimal.
Contohnya, jika diberikan:
GOOG
150.00
3
alert
155.00
logger
TradeLog
alert
140.00
160.50Output yang diharapkan:
ALERT: GOOG at $160.50 crossed threshold $155.00
TradeLog recorded GOOG: $160.50
ALERT: GOOG at $160.50 crossed threshold $140.00Jika diberikan:
AAPL
180.00
2
logger
DailyLog
alert
200.00
175.25Output yang diharapkan:
DailyLog recorded AAPL: $175.25
WATCHING: AAPL at $175.25 (threshold: $200.00)Perhatikan bahwa Stock tidak mengetahui apakah ia sedang memberi tahu alert atau logger. Stock hanya memanggil Update pada setiap observer. Pemisahan ini memudahkan kamu menambahkan tipe observer baru tanpa mengubah kode Stock!
Coba sendiri
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Baca nama saham
stockName, _ := reader.ReadString('\n')
stockName = strings.TrimSpace(stockName)
// Baca harga awal
initialPriceStr, _ := reader.ReadString('\n')
initialPrice, _ := strconv.ParseFloat(strings.TrimSpace(initialPriceStr), 64)
// Baca jumlah observer
numObserversStr, _ := reader.ReadString('\n')
numObservers, _ := strconv.Atoi(strings.TrimSpace(numObserversStr))
// TODO: Buat Stock baru menggunakan konstruktor NewStock
// TODO: Lakukan perulangan untuk setiap observer
for i := 0; i < numObservers; i++ {
// Baca tipe observer ("alert" atau "logger")
observerType, _ := reader.ReadString('\n')
observerType = strings.TrimSpace(observerType)
// Baca nilai konfigurasi
configValue, _ := reader.ReadString('\n')
configValue = strings.TrimSpace(configValue)
// TODO: Buat observer yang sesuai berdasarkan tipenya
// - Untuk "alert": ubah configValue menjadi ambang batas bertipe float64, lalu buat PriceAlert
// - Untuk "logger": gunakan configValue sebagai LogName, lalu buat PriceLogger
// TODO: Daftarkan observer ke saham
}
// Baca harga baru
newPriceStr, _ := reader.ReadString('\n')
newPrice, _ := strconv.ParseFloat(strings.TrimSpace(newPriceStr), 64)
// TODO: Tetapkan harga baru dan dapatkan tanggapan
// TODO: Cetak setiap tanggapan pada baris tersendiri
}
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 Generik13Pola Desain Bagian 1
Pengantar Pola DesainPola SingletonPola FactoryPola Abstract FactoryPola ObserverPola Strategy2Mendalami 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 PoolLatihan sendiri: Compiler Go online