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.
Görev
KolayFactory 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 olanDocumentsınıfını oluşturun.Ardından dört somut belge sınıfı oluşturun:
PDFReport—generate()metodu"Generating PDF Report"döndürürHTMLReport—generate()metodu"Generating HTML Report"döndürürPDFInvoice—generate()metodu"Generating PDF Invoice"döndürürHTMLInvoice—generate()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
createDocumentadı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 birstd::unique_ptr<Document>döndürmelidir. Geçersiz kombinasyonlar içinnullptrdöndürün.Ardından, iki saf sanal metoda sahip
DocumentFactoryadında bir soyut fabrika sınıfı oluşturun:createReport()— birstd::unique_ptr<Document>döndürürcreateInvoice()— birstd::unique_ptr<Document>döndürür
İki somut fabrika uygulayın:
PDFFactory—PDFReportvePDFInvoiceoluştururHTMLFactory—HTMLReportveHTMLInvoiceoluşturur
DocumentService.h: Soyut fabrikayı kullanan bir servis oluşturun.Yapıcısında (constructor) bir
std::unique_ptr<DocumentFactory>alan birDocumentServicesı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 üzerindegenerate()metodunu çağırarak sonucu her satıra bir tane gelecek şekilde yazdırangenerateAllDocuments()adında bir metodu olmalıdır.main.cpp: Her iki fabrika yaklaşımını da gösterin.İki girdi okuyun:
- Bir belge türü (
reportveyainvoice) - Bir format (
pdfveyahtml)
Ö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
DocumentServiceoluşturun (pdfiçinPDFFactory,htmliçinHTMLFactorykullanın). Soyut fabrikanın birbiriyle ilişkili belgeler ailesini nasıl oluşturduğunu göstermek içingenerateAllDocuments()metodunu çağırın.- Bir belge türü (
Örneğin, report ve pdf girdileriyle:
Generating PDF Report
Generating PDF Report
Generating PDF Invoiceinvoice ve html girdileriyle:
Generating HTML Invoice
Generating HTML Report
Generating HTML InvoiceBasit 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;
}
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
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner