Menu
Coddy logo textTech

Factory ve Abstract Factory

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 92 / 104.

Factory deseni, nesne oluşturma işlemini ayrı bir metoda veya sınıfa devreder ve tam sınıflarını belirtmeden nesneler oluşturmanıza olanak tanır. Bu, oluşturma mantığı karmaşık olduğunda veya istemci kodunu somut uygulamalardan ayırmak istediğinizde kullanışlıdır.

Basit bir fabrika (simple factory), girdiye bağlı olarak farklı türetilmiş türler döndüren bir yöntem kullanır:

#include <iostream>
#include <memory>

class Button {
public:
    virtual void render() = 0;
    virtual ~Button() = default;
};

class WindowsButton : public Button {
public:
    void render() override { std::cout << "Windows button\n"; }
};

class MacButton : public Button {
public:
    void render() override { std::cout << "Mac button\n"; }
};

// Factory function
std::unique_ptr<Button> createButton(const std::string& os) {
    if (os == "windows") return std::make_unique<WindowsButton>();
    if (os == "mac") return std::make_unique<MacButton>();
    return nullptr;
}

Abstract Factory, ilişkili nesne aileleri oluşturarak bu süreci bir adım daha ileri götürür. Tek bir fabrika metodu yerine, birden fazla oluşturma metoduna sahip bir fabrika arayüzünüz (interface) olur:

class Checkbox {
public:
    virtual void check() = 0;
    virtual ~Checkbox() = default;
};

class WindowsCheckbox : public Checkbox {
public:
    void check() override { std::cout << "Windows checkbox\n"; }
};

class MacCheckbox : public Checkbox {
public:
    void check() override { std::cout << "Mac checkbox\n"; }
};

// Abstract Factory
class GUIFactory {
public:
    virtual std::unique_ptr<Button> createButton() = 0;
    virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
    virtual ~GUIFactory() = default;
};

class WindowsFactory : public GUIFactory {
public:
    std::unique_ptr<Button> createButton() override {
        return std::make_unique<WindowsButton>();
    }
    std::unique_ptr<Checkbox> createCheckbox() override {
        return std::make_unique<WindowsCheckbox>();
    }
};

Esnek nesne oluşturmaya ihtiyaç duyduğunuzda Factory, ilgili nesnelerin birbiriyle tutarlı bir şekilde çalışmasını sağlamanız gerektiğinde ise Abstract Factory kullanın.

challenge icon

Görev

Kolay

Factory ve Abstract Factory desenlerini kullanarak bir Belge Oluşturucu (Document Generator) sistemi inşa edelim. Farklı çıktı formatları (PDF ve HTML) için farklı belge türleri (raporlar ve faturalar) oluşturabilen bir sistem kuracak ve fabrikaların nesne oluşturma işlemini istemci kodundan nasıl ayırdığını (decouple) göreceksiniz.

Kodunuzu dört dosya halinde düzenleyeceksiniz:

  • Document.h: Belge ürün hiyerarşinizi tanımlayın.

    Oluşturulan belgeyi açıklayan bir dize döndüren saf sanal (pure virtual) generate() metoduna ve sanal bir yıkıcıya (destructor) sahip soyut bir temel sınıf olan Document sınıfını oluşturun.

    Ardından dört somut belge sınıfı oluşturun:

    • PDFReportgenerate() metodu "Generating PDF Report" döndürür
    • HTMLReportgenerate() metodu "Generating HTML Report" döndürür
    • PDFInvoicegenerate() metodu "Generating PDF Invoice" döndürür
    • HTMLInvoicegenerate() metodu "Generating HTML Invoice" döndürür
  • DocumentFactory.h: Hem basit bir fabrika fonksiyonu hem de bir soyut fabrika (abstract factory) uygulayın.

    İlk olarak, iki dize alan createDocument adında basit bir fabrika fonksiyonu oluşturun: bir belge türü ("report" veya "invoice") ve bir format ("pdf" veya "html"). Bu fonksiyon, uygun somut belgeye işaret eden bir std::unique_ptr<Document> döndürmelidir. Geçersiz kombinasyonlar için nullptr döndürün.

    Ardından, iki saf sanal metoda sahip DocumentFactory adında bir soyut fabrika sınıfı oluşturun:

    • createReport() — bir std::unique_ptr<Document> döndürür
    • createInvoice() — bir std::unique_ptr<Document> döndürür

    İki somut fabrika uygulayın:

    • PDFFactoryPDFReport ve PDFInvoice oluşturur
    • HTMLFactoryHTMLReport ve HTMLInvoice oluşturur
  • DocumentService.h: Soyut fabrikayı kullanan bir servis oluşturun.

    Yapıcısında (constructor) bir std::unique_ptr<DocumentFactory> alan bir DocumentService sınıfı tanımlayın. Bu sınıfın, fabrikayı kullanarak hem bir rapor hem de bir fatura oluşturan ve ardından her birinin üzerinde generate() metodunu çağırarak sonucu her satıra bir tane gelecek şekilde yazdıran generateAllDocuments() adında bir metodu olmalıdır.

  • main.cpp: Her iki fabrika yaklaşımını da gösterin.

    İki girdi okuyun:

    1. Bir belge türü (report veya invoice)
    2. Bir format (pdf veya html)

    Önce, girdilere dayalı bir belge oluşturmak için basit fabrika fonksiyonunu kullanın. Üzerinde generate() metodunu çağırmanın sonucunu yazdırın.

    Ardından boş bir satır yazdırın.

    Sonra, format girdisine dayalı uygun fabrika ile bir DocumentService oluşturun (pdf için PDFFactory, html için HTMLFactory kullanın). Soyut fabrikanın birbiriyle ilişkili belgeler ailesini nasıl oluşturduğunu göstermek için generateAllDocuments() metodunu çağırın.

Örneğin, report ve pdf girdileriyle:

Generating PDF Report

Generating PDF Report
Generating PDF Invoice

invoice ve html girdileriyle:

Generating HTML Invoice

Generating HTML Report
Generating HTML Invoice

Basit fabrikanın her iki parametreye dayanarak tek bir belgeyi nasıl oluşturduğuna, soyut fabrikanın ise bir servis tarafından oluşturulan tüm belgelerin aynı formatı paylaşmasını nasıl sağladığına dikkat edin; bu, birlikte tutarlı bir şekilde çalışan ilgili nesne ailelerine ihtiyaç duyduğunuzda Abstract Factory deseninin sağladığı temel avantajdır.

Kopya kağıdı

Factory deseni, nesne oluşturma işlemini ayrı bir metoda veya sınıfa devreder ve tam sınıflarını belirtmeden nesneler oluşturmanıza olanak tanır. Bu, istemci kodunu somut uygulamalardan ayırır.

Basit bir fabrika (simple factory), girdiye göre farklı türetilmiş türler döndüren bir fonksiyon kullanır:

class Button {
public:
    virtual void render() = 0;
    virtual ~Button() = default;
};

class WindowsButton : public Button {
public:
    void render() override { std::cout << "Windows button\n"; }
};

class MacButton : public Button {
public:
    void render() override { std::cout << "Mac button\n"; }
};

// Factory function
std::unique_ptr<Button> createButton(const std::string& os) {
    if (os == "windows") return std::make_unique<WindowsButton>();
    if (os == "mac") return std::make_unique<MacButton>();
    return nullptr;
}

Abstract Factory deseni, birbiriyle ilişkili nesne aileleri oluşturur. Tek bir fabrika metodu yerine, birden fazla oluşturma metoduna sahip bir fabrika arayüzünüz olur:

class Checkbox {
public:
    virtual void check() = 0;
    virtual ~Checkbox() = default;
};

class WindowsCheckbox : public Checkbox {
public:
    void check() override { std::cout << "Windows checkbox\n"; }
};

class MacCheckbox : public Checkbox {
public:
    void check() override { std::cout << "Mac checkbox\n"; }
};

// Abstract Factory
class GUIFactory {
public:
    virtual std::unique_ptr<Button> createButton() = 0;
    virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
    virtual ~GUIFactory() = default;
};

class WindowsFactory : public GUIFactory {
public:
    std::unique_ptr<Button> createButton() override {
        return std::make_unique<WindowsButton>();
    }
    std::unique_ptr<Checkbox> createCheckbox() override {
        return std::make_unique<WindowsCheckbox>();
    }
};

Esnek nesne oluşturmaya ihtiyaç duyduğunuzda Factory kullanın. İlişkili nesnelerin bir aile olarak tutarlı bir şekilde birlikte çalışmasını sağlamanız gerektiğinde Abstract Factory kullanın.

Kendin dene

#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"

using namespace std;

int main() {
    // Girdileri oku
    string documentType;
    string format;
    cin >> documentType;
    cin >> format;
    
    // TODO: Bir belge oluşturmak için basit fabrika (simple factory) fonksiyonunu kullanın
    // documentType ve format girdilerine dayanarak
    // Üzerinde generate() çağırmanın sonucunu yazdırın
    
    // Boş bir satır yazdırın
    cout << endl;
    
    // TODO: Uygun fabrika ile bir DocumentService oluşturun
    // format girdisine dayanarak ("pdf" için PDFFactory, "html" için HTMLFactory)
    // Soyut fabrikayı (abstract factory) göstermek için generateAllDocuments() fonksiyonunu çağırın
    
    return 0;
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler