Design Patterns & Golang: Observer Pattern

Arda Coşar
4 min readJun 20, 2023

--

Giriş

Selamlar, design pattern yazı serimde özellikle gerçek hayattan basit örnekler seçmeye çalışıyorum. Çünkü sektörün içinden olsun diye seçilen karmaşık örnekler, hakim olmadığınız terimleri barındırabileceğinden ana fikrin anlaşılmasını zorlaştırabiliyor. Ayrıca sektördeki kullanım alanlarını vermenin kişiyi, pattern’i anlamaktan çok ezbere ittiğini düşünüyorum.

Hedefim, patternleri kullanmanız gerektiği yerlerde problemi tanımanız ve çözümü kendi şartlarınıza göre uyarlayabilecek seviyeye gelmeniz olacak.

Hikaye

Yorgun bir günün ardından akşam eve geldiniz ve gidip salonun lambasını açtınız. Daha sonrasında terlediğinizi farkettiniz ve gidip klimayı açtınız. Sonra da sıkıldığınızı farkettiniz ve televizyonu açtınız. Bunu her gün her gün yapmaktan çok sıkıldınız. Her seferinde oradan oraya yürüyor, küçük de olsa bir zahmete giriyorsunuz.

Bunları yapmak için belli başlı şeyleri de biliyor olmalısınız. Mesela lambanın yerini, mesela klimanın kumandasının yerini ve nasıl kullanılacağını, mesela televizyon kumandasının yer… neyse devamını tahmin etmişsinizdir. Kötü olan şu ki evinize misafir geldiğinde ve bunları yapmak istediğinde o da bunların hepsini bilmeli. Bu hayatta o kadar dertsizsiniz ki en büyük derdiniz bu olmuş ama şanslısınız. Çünkü siz bu hikayede zenginsiniz :)

Akıllı evler, teknoloji sevdalılarının bir noktada düşleyebildiği konforlu yaşam alanlarıdır. Siz ise bunu bir adım öteye taşıyarak satın aldınız. Artık kapınızın yanında çok güzel bir tuş var. Bir basıyorsunuz lambalar açılıyor, klimalar çalışıyor, televizyondan gelen sesler… Artık misafirlerinize de her şeyin bilgisini vermek zorunda değilsiniz. Tuşun yerini bilseler yeter. Çünkü şu anda sisteme bağlı ve ileride muhtemel bağlayabileceğiniz diğer ev aletleri kapınızın yanındaki butondan gelecek bir sinyali gözlemliyor.

Problem

  • Her şeyin tek tek açılması
  • Her seferinde yerlerinin bilinmesi gerektiği
  • Her misafire hepsinin yerlerini göstermesinin gerekmesi

Çözüm

  • İstenen her aletin tek bir tuşa bağlanması
  • Tek bir yerden girilen bilginin tüm eşyalara aktarılması

Teknik Giriş

Burada bütün iş açacağımız Akıllı ev yapısında bitiyor. Geri kalan objelerimiz çok sade ve anlaşılır. Bu yüzden modellemeye bizden sinyal bekleyen akıllı ürünlerimizle başlayabiliriz. Öncelikle bunları temsil edecek bir interface hazırlıyoruz

type AkilliUrun interface {
Aktifles()
Kapan()
}

Bu arayüzden de anlaşılacağı üzere akıllı ürünlerimden beklentim açılabilir ve kapanabilir olması, en azından şimdilik bu kadarı yeterli. Sırada bu arayüzü uygulayan akıllı ürünlerimi tanımlamak var.

type Klima struct{}

func (k Klima) Aktifles() {
fmt.Println("Klima açılıyor")
}

func (k Klima) Kapan() {
fmt.Println("Klima kapanıyor")
}
type Lamba struct{}

func (l Lamba) Aktifles() {
fmt.Println("Lamba açılıyor")
}

func (l Lamba) Kapan() {
fmt.Println("Lamba kapanıyor")
}
type Televizyon struct{}

func (t Televizyon) Aktifles() {
fmt.Println("Televizyon açılıyor")
}

func (t Televizyon) Kapan() {
fmt.Println("Televizyon kapanıyor")
}

Yukarıdaki örneklerde gördüğünüz gibi yapılarımız AkilliUrun arayüzünü uyguluyor. Bu sayede farklı tipteki nesneleri tek bir Array ya da Map içine alabilecek, hepsine aynı anda komut gönderebileceğiz.

Akıllı ev yapımızı kurarken adım adım gideceğim. Göreceğiniz üzere bu objem içinde Akıllı ürünlerimin olduğu bir map barındırıyor.

type AkilliEv struct {
urunler map[string]AkilliUrun
}

Liste de kullanabilirdik fakat ürünleri sistemden çıkarırken rahat olması için map kullanmayı tercih ettim. Sırada sistemimize ürün eklemek için yazacağımız fonksiyon var.

func (sk *AkilliEv) SistemeBagla(urun AkilliUrun) {
if sk.urunler == nil {
sk.urunler = make(map[string]AkilliUrun, 0)
}

urununAdi := reflect.TypeOf(urun).Name()
sk.urunler[urununAdi] = urun
fmt.Println("Sisteme baglandi:", urununAdi)
}

Eğer urunler objem hiç oluşmadıysa onu oluşturarak başlıyoruz. Sonrasında reflect.TypeOf.Name yardımı ile parametrede gönderilen ürünün adını elde ediyoruz ve bunu ürünler değişkenine kaydederken key olarak kullanıyoruz. Sistemden çıkarmak istediğim ürün olabilir diye urun çıkarmak için de bir fonksiyon hazırlayalım.

func (sk *AkilliEv) SistemdenCikar(urun AkilliUrun) {
urununAdi := reflect.TypeOf(urun).Name()
delete(sk.urunler, urununAdi)
fmt.Println("Sistemle baglantisi kesildi:", urununAdi)
}

Bu sayede ürünü parametre olarak verdiğimde sistemden çıkarmış oluyoruz. Gene üstte açıkladığım üzere ismiyle giderek ürünler değişkeninden çıkarıyoruz. Artık geriye asıl işi yapan fonksiyonlar kaldı.

func (sk *AkilliEv) HerseyiAc() {
fmt.Println("Sistem aktifleşiyor...")
for _, urun := range sk.urunler {
urun.Aktifles()
}
}

func (sk *AkilliEv) HerseyiKapat() {
fmt.Println("Sistem kapatılıyor...")
for _, urun := range sk.urunler {
urun.Kapan()
}
}

Her şeyi aç dediğimde içerisinde kayıtlı olan tüm ürünlerin Aktifles fonksiyonunu, Her şeyi kapat dediğimde içerisinde kayıtlı olan tüm ürünlerin Kapan fonksiyonunu çalıştırabileceğim şekilde geliştirmemi yaptım. Son olarak hikayemizi gerçekleştirmek için çalıştıracağımız ana kod bloğunu hazırlayabiliriz.

func OrnekCalistir() {
var (
akilliEv AkilliEv

lamba1 Lamba
televizyon1 Televizyon
klima1 Klima
)

akilliEv.SistemeBagla(lamba1)
akilliEv.SistemeBagla(televizyon1)
akilliEv.SistemeBagla(klima1)

akilliEv.HerseyiAc()
fmt.Println("------------------------")
akilliEv.HerseyiKapat()

fmt.Println("================================")

akilliEv.SistemdenCikar(klima1)

akilliEv.HerseyiAc()
fmt.Println("------------------------")
akilliEv.HerseyiKapat()
}

Burada görülebileceği üzere önce akilli ürünlerimi akilli ev sistemime ekliyorum. Daha sonrasında her şeyi aç dediğimde hepsinin açılmasını ve aynı şekilde hepsinin kapanmasını bekliyorum. Test etmek için bu işlemi, klimamı sistemden çıkardıktan sonra tekrar gerçekleştiriyorum. Bu kodu çalıştırdığımızda programımızın çıktısı şu şekilde olacaktır:

Sisteme baglandi: Lamba
Sisteme baglandi: Televizyon
Sisteme baglandi: Klima
Sistem aktifleşiyor…
Lamba açılıyor
Televizyon açılıyor
Klima açılıyor
— — — — — — — — — — — —
Sistem kapatılıyor…
Lamba kapanıyor
Televizyon kapanıyor
Klima kapanıyor
================================
Sistemle baglantisi kesildi: Klima
Sistem aktifleşiyor…
Lamba açılıyor
Televizyon açılıyor
— — — — — — — — — — — —
Sistem kapatılıyor…
Lamba kapanıyor
Televizyon kapanıyor

Sonuç

Artık bu dizaynın ne gibi problemlere çözüm getirdiğini biliyorsunuz. Burada olayın sadece açma kapama olarak anlaşılmasını istemem. Tüm klimaların derecesini aynı anda 2C° düşürmek de bu sisteme dahil edebilir. Burada amaç bir nesneyi gözlemleyen ve buna göre davranışını değiştiren başka nesneler yaratmak istersek nasıl bir yol izleyebileceğimizi göstermekti.

--

--