Design Patterns & Golang: Factory/Abstract Factory 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

Çok dağınık bir insansınız. Evde sürekli çorapların teki kayboluyor. Her sabah giyeceğiniz şeylerin yerini ararken çok zaman kaybettiğinizi fark ediyorsunuz. Artık eşyalarınızı koltukların ve yatakların üstüne fırlatmaktan bıktınız. Bu vesileyle medeniyete doğru yürümeye başladınız.

Evinize bir tane gardırop aldınız. Üstünüze giyebileceğiniz her şeyi artık burada toplama kararı verdiniz. Yeni aldığınız ürünler olursa onları da buraya ekleyebilirdiniz. Tshirt ya da Hırka aradığınızda gideceğiniz yeri biliyordunuz. Bir süre böyle devam ettikten sonra yeni bir sorunla karşılaştınız.

Sadece üstünüze giyebileceğiniz şeyleri koyduğunuzda sorun yoktu ama her tipten giysi, hatta ayakkabılarınızı bile koyduğunuzda artık yönetilemez olduğunu farkettiniz. Yaz’ları giyecek bir şey ararken Kışlık eşyaların arasında bulması zor oluyordu. Aynısı tam tersi için de geçerliydi.

Son zamanlarda düzene öyle hasret kaldmıştınızki bir gardırop daha aldınız. Artık hangi giysinizi nerede bulabileceğinizi biliyordunuz. Yaz’a uygun bir şeyler giymek istediğinizde Yaz Gardırobunuzu, Kış’a uygun bir şeyler giymek istediğinizde de Kış gardırobunuzu açtınız. Bundan sonra ne giyeceğinize karar verirken bilmeniz gereken tek şey bu sabah hangi gardıropu açacağınız.(Çok düz birisi olduğunuz için iki gardıropta da tek tip kombin var)

Problem

  • Giysilerin evin her yerine dağılmış olması ve yerlerinin bilinmemesi
  • Giysilerin gruplanmamış olması

Çözüm

  • Giysilerin tek bir yerde toplanması
  • Giysilerin gardırop kullanarak gruplanması

Teknik Giriş

Bugün çok klasik bir design pattern ile karşınızdayım. Ayrıca bu patternin iki aşaması olacak. İlk kısımda Factory pattern, ikinci kısımda ise Abstract Factory pattern hakkında fikir sahibi olmaya çalışacağız ki zaten hikayemiz de bu yönde ilerliyordu.

İlk olarak objelerimizi uyarlayacağımız, bu sayede farklı olasalar da tek bir tip üzerinden kabul ettirebilmemizi sağlayan arayüzü tasarlayarak başlayabiliriz.

type UstGiyilebilir interface {
UstuneGiy()
}

Oldukça açık bir şekilde üstümüze giyebileceğimiz giysilerimizin uygulayacağı bir arayüz hazırladık. Sırada bunu uygulayan objelerimizi yaratması var.

type Tshirt struct{}

func (t Tshirt) UstuneGiy() {
fmt.Println("Tshirt giyildi.")
}
type Hirka struct{}

func (h Hirka) UstuneGiy() {
fmt.Println("Hırka giyildi.")
}

Fonksiyonların işlevlerini temsilen basit bir log basma komutu ekledik. Sırada bunları temin edebileceğimiz Gardırop objemizi hazırlamak var.

type Gardirop struct{}

func (g Gardirop) UstGetir(mevsim string) UstGiyilebilir {
switch mevsim {
case "Yaz":
return &Tshirt{}
case "Kış":
return &Hirka{}
}

return nil
}

Argüman olarak aldığı mevsim değerinin eşleştiği seçeneğe göre doğru objeyi dönen tatlı bir uygulama yapmış olduk. Örneğimiz ise şu şekilde:

func OrnekCalistir() {
var (
ust UstGiyilebilir
gardirop Gardirop
)

ust = gardirop.UstGetir("Yaz")
ust.UstuneGiy()

fmt.Println("------------------------")

ust = gardirop.UstGetir("Kış")
ust.UstuneGiy()
}

Kısaca, verdiğimiz mevsime göre üstümüze giyecek bir şeyler dönüyor:

Tshirt giyildi.
— — — — — — — — — — — —
Hırka giyildi.

Yukarıdaki örnekte gördüğünüz üzere, tek tipte obje döneceğimiz zaman oldukça kullanışlı bir dizayn olsa da birden fazla obje için çok da yeterli olmadığını farkedeceksiniz. Burada artık Abstract Factory pattern’in kapısını çalıyoruz.

Bu noktadan sonra hedefimiz soyutlamayı bir kademe daha arttırmak olacak. Yani Gardırop objelerimiz de bir arayüze uyacak ve gardıroplar arasında geçişim de çok kolay bir hale gelecektir. Ancak onlara geçmeden önce gardıropun içinden alabileceğim objeleri tanımlayarak aradan çıkartmaya başlayabiliriz.

Üstümüze giyebileceğimiz şeyler için:

type UstGiyilebilir interface {
UstuneGiy()
}

type Tshirt struct{}

func (t Tshirt) UstuneGiy() {
fmt.Println("Tshirt giyildi.")
}

type Hirka struct{}

func (h Hirka) UstuneGiy() {
fmt.Println("Hırka giyildi.")
}

Altımıza giyebileceğimiz şeyler için:

type AltGiyilebilir interface {
AltinaGiy()
}

type Sort struct{}

func (s Sort) AltinaGiy() {
fmt.Println("Sort giyildi.")
}

type Pantolon struct{}

func (p Pantolon) AltinaGiy() {
fmt.Println("Pantolon giyildi.")
}

Ayağımıza giyebileceğimiz şeyler için:

type AyakGiyilebilir interface {
AyaginaGiy()
}

type Sandalet struct{}

func (s Sandalet) AyaginaGiy() {
fmt.Println("Sandalet giyildi.")
}

type Bot struct{}

func (b Bot) AyaginaGiy() {
fmt.Println("Bot giyildi.")
}

Her zaman olduğu gibi temsili fonksiyon işlevleriyle objelerimizi yarattık. Artık Gardıroplarımızı soyutlamak için ilk adımı atabiliriz.

type Gardirop interface {
UstGiysiGetir() UstGiyilebilir
AltGiysiGetir() AltGiyilebilir
AyakkabiGetir() AyakGiyilebilir
}

Gardıroplarımdan tek beklentim bana üstüm, altım ve ayağım için bir şeyler verebilmesi olacak. Bu şartı sağlayan iki adet gardırop objesi yaratalım.

Kışlık Gardırop:

type KislikGardirop struct{}

func (kg *KislikGardirop) UstGiysiGetir() UstGiyilebilir {
return Hirka{}
}

func (kg *KislikGardirop) AltGiysiGetir() AltGiyilebilir {
return Pantolon{}
}

func (kg *KislikGardirop) AyakkabiGetir() AyakGiyilebilir {
return Bot{}
}

Yazlık Gardırop:

type YazlikGardirop struct{}

func (yg *YazlikGardirop) UstGiysiGetir() UstGiyilebilir {
return Tshirt{}
}

func (yg *YazlikGardirop) AltGiysiGetir() AltGiyilebilir {
return Sort{}
}

func (yg *YazlikGardirop) AyakkabiGetir() AyakGiyilebilir {
return Sandalet{}
}

Gardıroplar kendilerinden beklenilen türde objeleri geri döndürmekten sorumlu oluyor. Bundan sonra ise tek bir adım kalıyor. İstediğim gardıropu bana dönen bir fonksiyona ihtiyacım var. Onu da şu şekilde hazırlıyoruz.

func (gm GardiropMerkezi) GardiropGetir(mevsim string) Gardirop {
switch mevsim {
case "Yaz":
return &YazlikGardirop{}
case "Kış":
return &KislikGardirop{}
}

return nil
}

Tanımlamamız gereken objeleri ve yardımcı fonksiyonları sonunda bitirdik. Artık ana kurgumuza geçip incelemeye başlayabiliriz.

func OrnekCalistir() {
var (
gardiropMerkezi GardiropMerkezi
gardirop Gardirop

ustGiysi UstGiyilebilir
altGiysi AltGiyilebilir
ayakkabi AyakGiyilebilir
)

gardirop = gardiropMerkezi.GardiropGetir("Yaz")
ustGiysi = gardirop.UstGiysiGetir()
altGiysi = gardirop.AltGiysiGetir()
ayakkabi = gardirop.AyakkabiGetir()
ustGiysi.UstuneGiy()
altGiysi.AltinaGiy()
ayakkabi.AyaginaGiy()

fmt.Println("------------------------")

gardirop = gardiropMerkezi.GardiropGetir("Kış")
gardirop.UstGiysiGetir().UstuneGiy()
gardirop.AltGiysiGetir().AltinaGiy()
gardirop.AyakkabiGetir().AyaginaGiy()
}

Örnekte görebileceğiniz üzere Yaz için bir gardırop istediğimde xxxGetir(). ismindeki fonksiyonlarım tercihimize uygun objeleri getirecek şekilde çalışacaktır. Kış dediğim takdirde de ona uygun objeleri elde etmiş olacağız.

Ayrıca üstteki örnekte obje, önce bir değişkene kaydedilip daha sonrasında kullanılıyorken alttaki örnekte obje üretilir üretilmez kendisine ait bir fonksiyona çağrı yapılıyor. İki farklı kullanımı da göstermek istedim. Bununla beraber bu serüveni programımızın çıktısı ile noktalayabiliriz:

Tshirt giyildi.
Sort giyildi.
Sandalet giyildi.
— — — — — — — — — — — —
Hırka giyildi.
Pantolon giyildi.
Bot giyildi.

Sonuç

Artık bu dizaynın ne gibi problemlere çözüm getirdiğini biliyorsunuz. Belli başlı tipte objelerin üretilmesine sık sık ihtiyaç duyuyorsanız bunun tek bir yerden yönetilmesini sağlamak sizin için sağlıklı olabilir. Aynı zamanda sisteme yeni objeler entegre etmeniz de oldukça kolay olacaktır.

--

--