Abstract Factory - Soyut Fabrika Tasarım Deseni
9 min read

Abstract Factory - Soyut Fabrika Tasarım Deseni

Abstract Factory - Soyut Fabrika Tasarım Deseni

Abstract Factory (Soyut Fabrika) tasarım deseni, concrete1abstract olmayan sınıfları tanımlamada kullanılan terim sınıfları belirlemeden ilgili nesnelerin ailelerini üretmenizi sağlayan creational kategorisindeki bir tasarım desenidir.

Daha önceki Tasarım Deseni / Kalıbı / Örüntüsü (Design Pattern) Nedir? başlıklı yazımızda tanıttığımız Creational Patterns altında yer alan Abstract Factory Method Nedir sorusunu ve kod örneklerini bu yazımızda oldukça detaylı paylaşıyoruz.

Bir mobilya mağazası simülatörü oluşturduğunuzu hayal edin. Kodunuz aşağıdakileri temsil eden sınıflardan oluşur:

  • İlgili ürünler ailesi, örneğin: SandalyeKanepe + Sehpa
  • Bu ailenin birkaç çeşidi vardır. Örneğin, Sandalye + Kanepe + Sehpa ürünleri şu varyantlarda mevcuttur: Modern, Victorian, ArtDeco
Ürün aileleri ve çeşitleri.

Aynı aileden diğer nesnelerle eşleşmeleri için bireysel mobilya nesneleri oluşturmanın bir yoluna ihtiyacınız var. Müşteriler, eşleşmeyen mobilyalar aldıklarında oldukça sinirleniyorlar.

Modern tarzdaki bir kanepe, Viktorya tarzı sandalyelerle eşleşmez.

Ayrıca, programa yeni ürünler veya ürün aileleri eklerken mevcut kodu değiştirmek istemezsiniz. Mobilya satıcıları kataloglarını çok sık günceller ve her seferinde temel kodu değiştirmek istemezsiniz.

Çözüm

Soyut Fabrika modelinin önerdiği ilk şey, ürün ailesinin her bir farklı ürünü için (örneğin, sandalye, kanepe veya sehpa) interfaceleri açıkça bildirmektir. Ardından tüm ürün çeşitlerinin bu interfaceleri takip etmesini sağlayabilirsiniz. Örneğin, tüm koltuk çeşitleri, Koltuk interface’ini uygulayabilir; tüm sehpa çeşitleri, CoffeeTable interface’ini vb. uygulayabilir.

Aynı nesnenin tüm varyantları tek bir sınıf hiyerarşisine taşınmalıdır.

Bir sonraki adım, ürün ailesinin parçası olan tüm ürünler için (örneğin, createChair, createSofa ve createCoffeeTable) oluşturma yöntemlerinin bir listesini içeren bir interface olan Soyut Fabrika‘yı tanımlamaktır. Bu yöntemler, daha önce çıkardığımız interfaceler tarafından temsil edilen soyut ürün türlerini döndürmelidir: Sandalye, Kanepe, Kahve Masası vb.

Her concrete factory belirli bir ürün çeşidine karşılık gelir.

Peki ya ürün çeşitleri? Bir ürün ailesinin her bir çeşidi için AbstractFactory interface’ine dayalı ayrı bir factory sınıfı oluşturuyoruz. Bir factory, belirli bir türden ürünleri döndüren bir sınıftır. Örneğin, ModernFurnitureFactory yalnızca ModernChair, ModernSofa ve ModernCoffeeTable nesneleri oluşturabilir.

İstemci kodu, ilgili soyut interfaceler aracılığıyla hem fabrikalar hem de ürünlerle çalışmak zorundadır. Bu, müşteri koduna ilettiğiniz bir fabrikanın türünü ve ayrıca müşteri kodunun aldığı ürün çeşidini gerçek müşteri kodunu bozmadan değiştirmenize olanak tanır.

Müşteri, birlikte çalıştığı fabrikanın concrete sınıfıyla ilgilenmemelidir.

Müşterinin sandalye üretmek için bir factory istediğini varsayalım. Müşteri, fabrikanın sınıfının farkında olmak zorunda değildir ve ne tür bir sandalye aldığı önemli değildir. İster Modern bir model ister Victoria tarzı bir sandalye olsun, müşteri soyut Sandalye interface’ini kullanarak tüm sandalyelere aynı şekilde davranmalıdır. Bu yaklaşımla müşterinin sandalye hakkında bildiği tek şey, sitOn yöntemini bir şekilde uyguladığıdır. Ayrıca, sandalyenin hangi çeşidi return edilirse edilsin, her zaman aynı fabrika nesnesi tarafından üretilen kanepe veya sehpa tipiyle eşleşecektir.

Açıklığa kavuşturulması gereken bir şey daha var:. Müşteri yalnızca soyut interfacelere maruz kalıyorsa, gerçek factory nesnelerini ne yaratır? Genellikle uygulama, initialitization aşamasında concrete bir factory nesnesi oluşturur. Bundan hemen önce uygulama, yapılandırmaya veya ortam ayarlarına bağlı olarak fabrika türünü seçmelidir.

Structure

  1. Abstract products, bir ürün ailesini oluşturan bir dizi farklı ancak ilgili ürün için interfaceler bildirir.
  2. Concrete products, varyantlara göre gruplandırılmış abstract ürünlerin çeşitli implementasyonlarıdır. Her abstract ürün (sandalye/kanepe), verilen tüm varyantlarda (Victoria/Modern) uygulanmalıdır.
  3. Abstract Factory interface’i, abstract ürünlerin her birini oluşturmak için bir dizi yöntem bildirir.
  4. Concrete Fabrikalar, abstarct fabrikanın yaratma yöntemlerini uygular. Her concrete fabrika, belirli bir ürün çeşidine karşılık gelir ve sadece bu ürün çeşitlerini yaratır.
  5. Abstract fabrikalar concrete ürünleri örneklendirselerde, yaratma yöntemlerinin imzaları karşılık gelen abstract ürünleri döndürmelidir. Bu şekilde, bir fabrikayı kullanan müşteri kodu, bir fabrikadan aldığı ürünün belirli bir çeşidiyle eşleşmez. Müşteri herhangi bir concrete fabrika ile çalışma şansı yakalar.

# Abstract Factory Pseudocode

Bu örnek, oluşturulan tüm öğeleri seçili bir işletim sistemiyle tutarlı tutarken, istemci kodunu concrete UI sınıflarına bağlamadan platformlar arası UI öğeleri oluşturmak için Soyut Fabrika modelinin nasıl kullanılabileceğini gösterir.

Cross-platform UI sınıfları örneği.

Bir cross-platform uygulamadaki aynı UI öğelerinin benzer şekilde davranması beklenir, ancak farklı işletim sistemlerinde biraz farklı görünür. Ayrıca, UI öğelerinin mevcut işletim sisteminin stiliyle eşleştiğinden emin olmak sizin işiniz. Programınızın Windows’ta yürütüldüğünde macOS denetimlerini oluşturmasını istemezsiniz.

Abstract Factory interface, istemci kodunun farklı türde UI öğeleri üretmek için kullanabileceği bir dizi oluşturma yöntemi bildirir. Concrete fabrikalar, belirli işletim sistemlerine karşılık gelir ve söz konusu işletim sistemine uyan UI öğelerini oluşturur.

Şu şekilde çalışır: Bir uygulama başlatıldığında mevcut işletim sisteminin türünü kontrol eder. Uygulama, işletim sistemiyle eşleşen bir sınıftan bir fabrika nesnesi oluşturmak için bu bilgileri kullanır. Kodun geri kalanı, UI öğeleri oluşturmak için bu fabrikayı kullanır. Bu, yanlış öğelerin oluşturulmasını önler.

Bu yaklaşımla, istemci kodu, abstract interfaceler aracılığıyla bu nesnelerle çalıştığı sürece, concrete fabrika sınıflarına ve UI öğelerine bağlı değildir. Bu ayrıca, istemci kodunun gelecekte ekleyebileceğiniz diğer fabrikaları veya UI öğelerini desteklemesine de olanak tanır.

Sonuç olarak, uygulamanıza her yeni UI öğesi varyasyonu eklediğinizde istemci kodunu değiştirmeniz gerekmez. Bu öğeleri üreten yeni bir fabrika sınıfı oluşturmanız ve uygun olduğunda o sınıfı seçmesi için uygulamanın başlatma kodunu biraz değiştirmeniz yeterlidir.

// Soyut fabrika arabirimi, farklı soyut ürünler döndüren bir dizi method bildirir. 
// Bu ürünlere aile denir ve üst düzey bir tema veya konseptle ilişkilidir. 
// Bir ailenin ürünleri genellikle kendi aralarında işbirliği yapabilir. 
// Bir ürün ailesinin birkaç çeşidi olabilir, ancak bir çeşidin ürünleri 
// başka bir çeşidin ürünleri ile uyumlu değildir.
interface GUIFactory is
    method createButton():Button
    method createCheckbox():Checkbox

// Concrete fabrikalar, tek bir varyanta ait bir ürün ailesi üretir. 
// Fabrika, ortaya çıkan ürünlerin uyumlu olduğunu garanti eder. 
// Concrete fabrikanın yöntemlerinin imzaları abstract bir ürün döndürürken, 
// yöntemin içinde concrete bir ürün örneklendirilir.
class WinFactory implements GUIFactory is
    method createButton():Button is
        return new WinButton()
    method createCheckbox():Checkbox is
        return new WinCheckbox()

// Her concrete fabrikanın karşılık gelen bir ürün çeşidi vardır.
class MacFactory implements GUIFactory is
    method createButton():Button is
        return new MacButton()
    method createCheckbox():Checkbox is
        return new MacCheckbox()

// Bir ürün ailesinin her farklı ürününün bir temel interface'i olmalıdır. 
// Ürünün tüm çeşitleri bu interface'i uygulamalıdır.
interface Button is
    method paint()

// Concrete ürünler, ilgili concrete fabrikalar tarafından oluşturulur.
class WinButton implements Button is
    method paint() is
        // Render a button in Windows style.

class MacButton implements Button is
    method paint() is
        // Render a button in macOS style.

// İşte başka bir ürünün temel interface'i. 
// Tüm ürünler birbirleriyle etkileşime girebilir, 
// ancak uygun etkileşim yalnızca aynı concrete varyantın ürünleri arasında mümkündür.
interface Checkbox is
    method paint()

class WinCheckbox implements Checkbox is
    method paint() is
        // Render a checkbox in Windows style.

class MacCheckbox implements Checkbox is
    method paint() is
        // Render a checkbox in macOS style.

// İstemci kodu fabrikalar ve ürünlerle yalnızca soyut türler aracılığıyla çalışır: 
// GUIFactory, Button ve Checkbox. Bu, herhangi bir fabrika veya ürün alt sınıfını, 
// onu bozmadan müşteri koduna geçirmenizi sağlar.
class Application is
    private field factory: GUIFactory
    private field button: Button
    constructor Application(factory: GUIFactory) is
        this.factory = factory
    method createUI() is
        this.button = factory.createButton()
    method paint() is
        button.paint()

// Uygulama, mevcut konfigürasyona veya ortam ayarlarına bağlı olarak fabrika tipini seçer
// ve çalışma zamanında (genellikle başlatma aşamasında) oluşturur.
class ApplicationConfigurator is
    method main() is
        config = readApplicationConfigFile()

        if (config.OS == "Windows") then
            factory = new WinFactory()
        else if (config.OS == "Mac") then
            factory = new MacFactory()
        else
            throw new Exception("Error! Unknown operating system.")

        Application app = new Application(factory)

💡 Uygulanabilirlik

🐛 Kodunuzun çeşitli ilgili ürün aileleriyle çalışması gerektiğinde, ancak bu ürünlerin concrete sınıflara bağlı olmasını istemiyorsanız, bunlar önceden bilinmiyor olabilir veya yalnızca gelecekteki genişletilebilirliğe izin vermek istediğinizde Soyut Fabrikayı kullanın.

⚡ Abstract Factory, ürün ailesinin her sınıfından nesneler oluşturmak için size bir interface sağlar. Kodunuz bu interface aracılığıyla nesneler oluşturduğu sürece, uygulamanız tarafından halihazırda oluşturulmuş ürünlerle eşleşmeyen bir ürünün yanlış varyantını oluşturma konusunda endişelenmenize gerek yoktur.

  • Birincil sorumluluğunu bulanıklaştıran bir dizi Fabrika Yöntemi içeren bir sınıfınız olduğunda Soyut Fabrikayı uygulamayı düşünün.
  • İyi tasarlanmış bir programda her sınıf sadece bir şeyden sorumlu olmalıdır. Bir sınıf birden fazla ürün türüyle uğraştığında, fabrika yöntemlerini bağımsız bir fabrika sınıfına veya tam gelişmiş bir Soyut Fabrika uygulamasına extract etmeye değerdir.

🗎 Nasıl Implemente Edeceğiz?

  1. Ürünlerin çeşitlerine karşı farklı ürün türlerinden oluşan bir matrisin haritasını çıkarın.
  2. Tüm ürün türleri için abstract ürün arayüzleri bildirin. Ardından, tüm concrete ürün sınıflarının bu interfaceleri uygulamasını sağlayın.
  3. Tüm abstract ürünler için bir dizi oluşturma yöntemiyle soyut fabrika interfaceini tanımlayın.
  4. Her ürün çeşidi için bir tane olmak üzere bir dizi concrete fabrika sınıfı implemente edin.
  5. Uygulamada bir yerde fabrika initialization kodu oluşturun. Uygulama yapılandırmasına veya mevcut ortama bağlı olarak, concrete fabrikası sınıflarından birini başlatması gerekir. Bu fabrika nesnesini, ürünleri oluşturan tüm sınıflara geçirin.
  6. Kodu tarayın ve ürün constructorlarına yapılan tüm doğrudan erişimleri bulun. Bunları fabrika nesnesindeki uygun oluşturma yöntemine yapılan çağrılarla değiştirin.

⚖️ Abstract Factory Method Avantajları ve Dezavantajları

  • ✔️ Bir fabrikadan aldığınız ürünlerin birbiriyle uyumlu olduğundan emin olabilirsiniz.
  • ✔️Concrete ürünler ve müşteri kodu arasındaki tight coupling’den kaçınırsınız.
  • ✔️ Single Responsibility Principle. Ürün oluşturma kodunu tek bir yere extract ederek kodun desteklenmesini kolaylaştırabilirsiniz.
  • ✔️ Open/Closed Principle. Mevcut müşteri kodunu bozmadan yeni ürün çeşitlerini tanıtabilirsiniz.
  • ❌ Desenle birlikte birçok yeni interface ve sınıf tanıtıldığından, kod olması gerekenden daha karmaşık hale gelme ihtimali var.

⇄ Diğer Desenlerle İlişkisi

  • Birçok tasarım, Factory Method (daha az karmaşık ve alt sınıflar aracılığıyla daha fazla özelleştirilebilir) kullanılarak başlar ve Abstract Factory, Prototype veya Builder (daha esnek, ancak daha karmaşık) desene doğru gelişir.
  • Builder, adım adım karmaşık nesneler oluşturmaya odaklanır. Abstract Factory, ilgili nesnelerin ailelerini oluşturma konusunda uzmanlaşmıştır. Abstract Factory ürünü hemen return ederken, Builder ürünü getirmeden önce bazı ek oluşturma adımlarını çalıştırmanıza izin verir.
  • Soyut Fabrika sınıfları genellikle bir dizi Factory Methoda dayanır, ancak bu sınıflarda yöntemleri oluşturmak için Prototype da kullanabilirsiniz.
  • Soyut Fabrika, yalnızca alt sistem nesnelerinin oluşturulma şeklini istemci kodundan gizlemek istediğinizde Facade desene bir alternatif olarak hizmet eder.
  • Abstract Factory‘i, Bridge ile birlikte kullanabilirsiniz. Bu eşleştirme, Bridge tarafından tanımlanan bazı soyutlamalar yalnızca belirli uygulamalarla çalışabildiğinde kullanışlıdır. Bu durumda, Abstract Factory bu ilişkileri içine alabilir ve karmaşıklığı istemci kodundan gizleyebilir.
  • Abstract Factory, Builder ve Prototype‘ların tümü Singleton olarak uygulanır.

<> Kod Örneği

Abstract Factory Method Example

Aşağıdaki public repo adresine giderek adresine giderek örnek Abstract Factory method kodlarını inceleyebilirsiniz.

Github repoya gitmek için Tıklayınız.