# Observer Pattern

Mari kita bahas *Observer Pattern*, salah satu pola desain perilaku (*behavioral pattern*) yang sangat populer selain *Strategy Pattern*.

*Observer Pattern* adalah pola desain di mana sebuah objek, yang disebut subject, mengelola daftar objek lain yang bergantung padanya, yang disebut observers. *Subject* secara otomatis akan memberi tahu semua *observers*-nya setiap kali ada perubahan keadaan (*state change*).

Analogi Sederhana: 📢

Bayangkan Anda berlangganan (*subscribe*) sebuah kanal YouTube.

* Kanal YouTube adalah Subject.
* Anda (dan subscriber lainnya) adalah Observers.

Ketika kanal YouTube mengunggah video baru (ini adalah "perubahan keadaan"), YouTube secara otomatis mengirimkan notifikasi kepada Anda dan semua subscriber lainnya. Kanal tidak perlu tahu siapa saja Anda secara spesifik, ia hanya tahu memiliki daftar subscriber yang harus diberi tahu. Anda pun tidak perlu terus-menerus memeriksa kanal tersebut; Anda akan diberi tahu secara otomatis.

Inilah inti dari *Observer Pattern*: menciptakan hubungan satu-ke-banyak (*one-to-many*) antara objek, sehingga jika satu objek berubah, semua yang bergantung padanya akan diperbarui secara otomatis.

***

#### ## Implementasi di Go

Dalam Go, kita akan menggunakan *interface* untuk mendefinisikan peran *Subject* dan *Observer*.

Skenario: Sebuah toko online (*Store*) sebagai *Subject*. Pelanggan (*Customer*) sebagai *Observers* yang tertarik pada produk baru. Ketika produk baru tiba di toko, semua pelanggan yang terdaftar akan mendapatkan notifikasi.

**1. Definisikan Interface**

```go
package main

// Observer Interface
type Observer interface {
    Update(productName string)
    GetID() string
}

// Subject Interface
type Subject interface {
    Register(observer Observer)
    Deregister(observer Observer)
    NotifyAll()
}
```

**2. Buat Concrete Subject dan Concrete Observer**

`Store` adalah *Concrete Subject* dan `Customer` adalah *Concrete Observer*.

```go
package main

import "fmt"

// --- Concrete Subject ---
type Store struct {
    observers   []Observer
    productName string
}

func NewStore() *Store {
    return &Store{
        observers: make([]Observer, 0),
    }
}

func (s *Store) Register(observer Observer) {
    fmt.Printf("Menambahkan pelanggan %s ke daftar notifikasi\n", observer.GetID())
    s.observers = append(s.observers, observer)
}

func (s *Store) Deregister(observer Observer) {
    s.observers = removeFromSlice(s.observers, observer)
}

func (s *Store) NotifyAll() {
    fmt.Println("\n--- Mengirim notifikasi ke semua pelanggan ---")
    for _, observer := range s.observers {
        observer.Update(s.productName)
    }
}

// Method untuk memicu notifikasi
func (s *Store) NewProductArrived(productName string) {
    fmt.Printf("\nProduk baru telah tiba: %s!\n", productName)
    s.productName = productName
    s.NotifyAll()
}

// Helper function untuk menghapus observer
func removeFromSlice(observers []Observer, observerToRemove Observer) []Observer {
    for i, observer := range observers {
        if observer.GetID() == observerToRemove.GetID() {
            return append(observers[:i], observers[i+1:]...)
        }
    }
    return observers
}


// --- Concrete Observer ---
type Customer struct {
    name string
}

func (c *Customer) Update(productName string) {
    fmt.Printf("Hai %s, produk '%s' sekarang tersedia di toko!\n", c.name, productName)
}

func (c *Customer) GetID() string {
    return c.name
}
```

**3. Penggunaan di Client (`main.go`)**

```go
package main

func main() {
    // Subject
    appleStore := NewStore()

    // Observers
    customerA := &Customer{name: "Andi"}
    customerB := &Customer{name: "Budi"}
    customerC := &Customer{name: "Citra"}

    // Pelanggan mendaftar untuk notifikasi
    appleStore.Register(customerA)
    appleStore.Register(customerB)
    appleStore.Register(customerC)

    // Toko merilis produk baru, notifikasi terkirim otomatis
    appleStore.NewProductArrived("iPhone 17")
	
    // Budi berhenti berlangganan
    appleStore.Deregister(customerB)

    // Toko merilis produk baru lagi
    appleStore.NewProductArrived("MacBook Pro M5")
}
```

Output Go:

```
Menambahkan pelanggan Andi ke daftar notifikasi
Menambahkan pelanggan Budi ke daftar notifikasi
Menambahkan pelanggan Citra ke daftar notifikasi

Produk baru telah tiba: iPhone 17!

--- Mengirim notifikasi ke semua pelanggan ---
Hai Andi, produk 'iPhone 17' sekarang tersedia di toko!
Hai Budi, produk 'iPhone 17' sekarang tersedia di toko!
Hai Citra, produk 'iPhone 17' sekarang tersedia di toko!

Produk baru telah tiba: MacBook Pro M5!

--- Mengirim notifikasi ke semua pelanggan ---
Hai Andi, produk 'MacBook Pro M5' sekarang tersedia di toko!
Hai Citra, produk 'MacBook Pro M5' sekarang tersedia di toko!
```

***

#### ## Implementasi di Java

Implementasi di Java sangat mirip secara konsep, menggunakan `interface` dan kelas.

**1. Definisikan Interface**

Observer.java

```
public interface Observer {
    void update(String productName);
}
```

Subject.java

```
public interface Subject {
    void register(Observer observer);
    void deregister(Observer observer);
    void notifyObservers();
}
```

**2. Buat Concrete Subject dan Concrete Observer**

Store.java (Concrete Subject)

```
import java.util.ArrayList;
import java.util.List;

public class Store implements Subject {
    private String productName;
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void register(Observer observer) {
        System.out.println("Menambahkan observer ke daftar notifikasi.");
        observers.add(observer);
    }

    @Override
    public void deregister(Observer observer) {
        System.out.println("Menghapus observer dari daftar notifikasi.");
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        System.out.println("\n--- Mengirim notifikasi ke semua observer ---");
        for (Observer observer : observers) {
            observer.update(productName);
        }
    }
    
    // Method untuk memicu notifikasi
    public void newProductArrived(String productName) {
        this.productName = productName;
        System.out.printf("\nProduk baru (%s) telah tiba!\n", productName);
        notifyObservers();
    }
}
```

Customer.java (Concrete Observer)

```
public class Customer implements Observer {
    private String name;

    public Customer(String name) {
        this.name = name;
    }

    @Override
    public void update(String productName) {
        System.out.printf("Hai %s, produk '%s' sekarang tersedia!\n", this.name, productName);
    }
}
```

**3. Penggunaan di Client (`Main.java`)**

```
public class Main {
    public static void main(String[] args) {
        // Subject
        Store appleStore = new Store();

        // Observers
        Observer customerA = new Customer("Andi");
        Observer customerB = new Customer("Budi");
        Observer customerC = new Customer("Citra");

        // Pelanggan mendaftar
        appleStore.register(customerA);
        appleStore.register(customerB);
        appleStore.register(customerC);
        
        // Produk baru datang
        appleStore.newProductArrived("iPhone 17");

        // Budi berhenti berlangganan
        appleStore.deregister(customerB);
        
        // Produk baru lainnya datang
        appleStore.newProductArrived("MacBook Pro M5");
    }
}
```

Output Java:

```
Menambahkan observer ke daftar notifikasi.
Menambahkan observer ke daftar notifikasi.
Menambahkan observer ke daftar notifikasi.

Produk baru (iPhone 17) telah tiba!

--- Mengirim notifikasi ke semua observer ---
Hai Andi, produk 'iPhone 17' sekarang tersedia!
Hai Budi, produk 'iPhone 17' sekarang tersedia!
Hai Citra, produk 'iPhone 17' sekarang tersedia!
Menghapus observer dari daftar notifikasi.

Produk baru (MacBook Pro M5) telah tiba!

--- Mengirim notifikasi ke semua observer ---
Hai Andi, produk 'MacBook Pro M5' sekarang tersedia!
Hai Citra, produk 'MacBook Pro M5' sekarang tersedia!
```
