Prototype Pattern

Prototype Pattern, mevcut nesneleri sıfırdan oluşturmak yerine kopyalayarak yeni nesneler oluşturmak için kullanılan yaratıcı bir tasarım modelidir. Nesne oluşturmanın pahalı, karmaşık olduğu veya küçük farklarla birçok benzer nesneye ihtiyaç duyulduğu durumlarda kullanışlıdır.

Object-Oriented Programming'da Prototype Pattern, bir nesnenin başka nesneler oluşturmak için şablon görevi görmesine olanak tanır. Uygulama, bir fabrikadan veya kurucudan adım adım yeni bir nesne oluşturmasını istemek yerine, mevcut bir nesneyi klonlar ve ardından yalnızca değişmesi gereken parçaları değiştirir.

Giriş

Bu Design Patterns serisinin önceki makalelerinde Factory Pattern, Abstract Factory Pattern ve Builder Pattern'yu tartışmıştık. Bunların hepsi yaratıcı Design Patternsdır, ancak her biri farklı bir nesne yaratma problemini çözer.

Factory Pattern hangi nesnenin yaratılması gerektiğine karar verir. Abstract Factory Pattern, ilgili nesnelerin ailelerini oluşturur. Builder Pattern karmaşık nesneleri adım adım oluşturur. Prototype Pattern, mevcut bir nesneyi kopyalayarak yeni nesneler oluşturur.

Bu model özellikle nesne başlatmanın birçok değer, database verileri, yapılandırma, API yanıtları veya pahalı kurulum mantığı gerektirdiği durumlarda kullanışlıdır. Uygulama, hazır bir nesneyi klonlayarak aynı yapım sürecinin birçok kez tekrarlanmasını önleyebilir.

Prototype Pattern Nedir?

Prototype Pattern, prototip adı verilen mevcut bir nesneyi kopyalayarak nesneler oluşturan bir tasarım desenidir. Klonlanan nesne, orijinal nesneyle aynı başlangıç ​​durumuna sahip yeni bir örnek haline gelir.

Basit bir ifadeyle prototip, hazır bir örnektir. Uygulama benzer bir nesneye ihtiyaç duyduğunda örneği kopyalar ve kopyayı özelleştirir.

Örneğin, bir document düzenleyicisi bir prototip fatura şablonunu kullanabilir. Yeni bir faturaya ihtiyaç duyulduğunda sistem, fatura şablonunu kopyalar ve müşteri ayrıntılarını, fatura numarasını ve tarihleri ​​değiştirir.

Prototype Pattern'nun Ana Fikri

Prototype Pattern'nun ana fikri inşa etmek yerine kopyalamaya dayanmaktadır. Sıfırdan yeni bir nesne oluştururken tekrarlanan kurulum adımları gerektiğinde bu yararlı olabilir.

Desen genellikle şunları içerir:

  • Zaten varsayılan değerleri veya hazırlanmış konfigürasyonu içeren bir prototip nesnesi.

  • Nesnenin bir kopyasını oluşturmaya yönelik bir klonlama mekanizması.

  • Klonlanan nesnenin oluşturulduktan sonra isteğe bağlı olarak özelleştirilmesi.

Bu yaklaşım, nesne oluşturmayı belirli durumlarda daha hızlı, daha basit ve daha tutarlı hale getirebilir.

Prototype Pattern Neden Önemlidir?

Prototype Pattern önemlidir çünkü nesne oluşturma işlemi bazen pahalı olabilir veya tekrarlanabilir olabilir. Bazı nesneler karmaşık kurulum, birçok kurucu parametresi, iç içe geçmiş nesneler veya varsayılan yapılandırmalar gerektirir.

Aynı kurulum birçok kez tekrarlanırsa kodun bakımı zorlaşır. Prototype Pattern, geliştiricilerin bir nesneyi bir kez hazırlayıp şablon olarak yeniden kullanmasına olanak tanır.

Bu, bazı durumlarda performansı artırabilir ve birçok nesne aynı temel yapıyı paylaştığında kod organizasyonunu iyileştirebilir.

Prototype Pattern Olmadan Sorun

Rapor şablonları oluşturan bir uygulama düşünün. Her raporun bir başlığı, düzeni, bölümleri, meta verileri, filtreleri, stil ayarları ve dışa aktarma seçenekleri bulunur. Her raporun sıfırdan oluşturulması birçok tekrarlanan adımı gerektirebilir.

$report = new Report();
$report->setLayout('professional');
$report->setFont('Arial');
$report->setPageSize('A4');
$report->addSection('Summary');
$report->addSection('Details');
$report->enableCharts();
$report->setExportFormat('pdf');

Birçok rapor aynı temel yapılandırmayı kullanıyorsa bu adımların birden fazla yerde tekrarlanması temiz değildir. Prototype Pattern ile uygulama, hazırlanmış bir rapor nesnesi oluşturabilir ve benzer bir rapora ihtiyaç duyulduğunda onu kopyalayabilir.

Prototype Pattern ve Klonlama

Klonlama, bir nesnenin kopyasını oluşturma işlemidir. PHP'da nesneler clone anahtar sözcüğü kullanılarak klonlanabilir.

$copy = clone $original;

Kopyalanan nesne yeni bir örnektir ancak başlangıçta orijinal nesneyle aynı özellik değerlerini içerir.

Ancak geliştiricilerin sığ kopya ile derin kopya arasındaki farkı anlamaları gerekir. Nesnelerin içinde başka nesneler bulunduğunda bu çok önemlidir.

PHP'da Temel Prototype Örneği

Aşağıdaki örnek, PHP'daki basit bir Prototype Pattern uygulamasını göstermektedir:

class ReportTemplate
{
    public string $title;
    public string $layout;
    public array $sections = [];

    public function __construct(string $title, string $layout)
    {
        $this->title = $title;
        $this->layout = $layout;
    }

    public function addSection(string $section): void
    {
        $this->sections[] = $section;
    }
}

$defaultReport = new ReportTemplate('Default Report', 'professional');
$defaultReport->addSection('Summary');
$defaultReport->addSection('Details');

$monthlyReport = clone $defaultReport;
$monthlyReport->title = 'Monthly Sales Report';

Bu örnekte, varsayılan rapor prototip görevi görmektedir. Aylık rapor, varsayılan raporun kopyalanması ve başlığın değiştirilmesiyle oluşturulur.

PHP'da __clone kullanımı

PHP, __clone adı verilen özel bir sihirli yöntem sağlar. Bu yöntem, bir nesne klonlandıktan sonra otomatik olarak çağrılır. Geliştiriciler bunu değerleri sıfırlamak, iç içe geçmiş nesneleri kopyalamak veya klonlanmış nesneyi hazırlamak için kullanabilir.

class Document
{
    public string $title;
    public string $documentId;

    public function __construct(string $title)
    {
        $this->title = $title;
        $this->documentId = uniqid('doc_', true);
    }

    public function __clone()
    {
        $this->documentId = uniqid('doc_', true);
    }
}

$original = new Document('Invoice Template');
$copy = clone $original;

echo $original->documentId;
echo $copy->documentId;

Bu örnekte, klonlanan document yeni bir document kimliği alır. Bu, orijinal ve klonlanmış documents'nun aynı tanımlayıcıyı paylaşmasını engeller.

Sığ Kopyalama

Sığ bir kopya, nesnenin kendisinin kopyalandığı ancak içindeki iç içe geçmiş nesnelerin referans yoluyla paylaşılmaya devam ettiği anlamına gelir. Bu, klonlanan nesnenin iç içe geçmiş bir nesneyi değiştirmesi durumunda beklenmeyen davranışlara neden olabilir.

Örneğin, bir Rapor nesnesi bir Stil nesnesi içeriyorsa, raporun kopyalanması rapor nesnesini kopyalayabilir ancak yine de aynı Stil nesnesine referans verebilir. Klonlanan raporun stili değişirse orijinal rapor da etkilenebilir.

Bu nedenle geliştiricilerin, başka nesneler içeren nesneleri klonlarken dikkatli olmaları gerekir.

PHP'da Sığ Kopyalama Örneği

class Style
{
    public string $font = 'Arial';
}

class Report
{
    public Style $style;

    public function __construct()
    {
        $this->style = new Style();
    }
}

$reportA = new Report();
$reportB = clone $reportA;

$reportB->style->font = 'Calibri';

echo $reportA->style->font;

Bu örnekte yazı tipini raporB aracılığıyla değiştirmek raporA'yı da etkileyebilir çünkü her iki rapor da yüzeysel bir kopyalamadan sonra aynı Style nesnesini paylaşabilir.

Derin Kopya

Derin kopya, nesnenin ve onun iç içe geçmiş nesnelerinin kopyalandığı anlamına gelir. Bu, orijinal nesnenin ve klonlanan nesnenin dahili nesneleri referans yoluyla paylaşmasını engeller.

PHP'da derin kopyalama, iç içe geçmiş nesnelerin klonlanmasıyla __clone yönteminin içinde manuel olarak işlenebilir.

PHP'da Derin Kopya Örneği

class Style
{
    public string $font = 'Arial';
}

class Report
{
    public Style $style;

    public function __construct()
    {
        $this->style = new Style();
    }

    public function __clone()
    {
        $this->style = clone $this->style;
    }
}

$reportA = new Report();
$reportB = clone $reportA;

$reportB->style->font = 'Calibri';

echo $reportA->style->font;

Bu örnekte reportB, Style nesnesinin kendi kopyasını alıyor. Klonlanmış rapor stilinin değiştirilmesi orijinal rapor stilini etkilemez.

Prototype Arayüzü

Bazı uygulamalar klon yöntemiyle bir prototip arayüzü tanımlar. Bu, modeli açık hale getirir ve farklı prototip sınıflarının aynı sözleşmeyi takip etmesine olanak tanır.

interface Prototype
{
    public function clone(): Prototype;
}

class InvoiceTemplate implements Prototype
{
    public function __construct(
        public string $title,
        public array $items = []
    ) {
    }

    public function clone(): Prototype
    {
        return clone $this;
    }
}

Bu yapı, uygulamanın ortak bir arayüz üzerinden farklı prototip nesnelerle çalıştığı durumlarda faydalı olabilir.

Gerçek Dünyadan Örnek: Fatura Şablonu

Faturalar Prototype Pattern'nun pratik bir örneğidir. Birçok fatura aynı yapıyı, vergi kurallarını, alt bilgi metnini, para birimini ve düzeni paylaşabilir. Sistem, faturayı her seferinde sıfırdan oluşturmak yerine önceden tanımlanmış bir fatura şablonunu kopyalayabilir.

class Invoice
{
    public string $invoiceNumber;
    public string $customerName;
    public string $currency = 'USD';
    public array $items = [];
    public string $footer = 'Thank you for your business.';

    public function __construct()
    {
        $this->invoiceNumber = uniqid('INV-', true);
    }

    public function __clone()
    {
        $this->invoiceNumber = uniqid('INV-', true);
        $this->items = [];
        $this->customerName = '';
    }

    public function addItem(string $name, float $price): void
    {
        $this->items[] = [
            'name' => $name,
            'price' => $price,
        ];
    }
}

$template = new Invoice();

$invoice = clone $template;
$invoice->customerName = 'John Doe';
$invoice->addItem('Web Development Service', 500);

Bu örnekte, fatura şablonu varsayılan para birimi ve alt bilgi değerlerini sağlar. Klonlanan fatura yeni bir fatura numarası alır ve belirli bir müşteri için özelleştirilebilir.

Gerçek Dünyadan Örnek: Oyun Nesneleri

Prototype Pattern oyun geliştirmede yaygındır. Bir oyunun birçok benzer düşman, silah, öğe veya karakter yaratması gerekebilir. Oyun, her nesneyi sıfırdan yapılandırmak yerine hazırlanan prototipleri klonlayabiliyor.

Örneğin bir oyun, varsayılan sağlık, hız, hasar ve görünüme sahip temel bir düşman için bir prototip tanımlayabilir. Oyun daha fazla düşmana ihtiyaç duyduğunda prototipi klonlar ve konum veya seviye gibi küçük değerleri değiştirir.

Bu, nesne oluşturmayı daha verimli ve yönetilmesi daha kolay hale getirir.

Gerçek Dünyadan Örnek: Belge Şablonları

Belge sistemleri genellikle sözleşmeler, raporlar, faturalar, sertifikalar ve mektuplar için şablonlar kullanır. Prototype Pattern bu duruma doğal olarak uyuyor çünkü bir şablon kopyalanabiliyor ve özelleştirilebiliyor.

Örneğin, bir sertifika şablonu varsayılan düzeni, logoyu, başlığı, imza alanını ve stili içerebilir. Sistem şablonu kopyalar ve alıcının adını, tarihini ve sertifika numarasını günceller.

Bu, şablon yapılandırmasının tekrarlanmasını önler ve oluşturulan documents genelinde tutarlılık sağlar.

Prototype Pattern vs Factory Pattern

Prototype Pattern ve Factory Pattern'nun her ikisi de yaratıcı Design Patternsdır, ancak nesneleri farklı şekilde yaratırlar.

Factory Pattern, bir sınıfı seçip örnekleyerek nesneler oluşturur. Genellikle yapıcıları kullanır ve girdiye veya konfigürasyona göre sınıfa karar verebilir.

Prototype Pattern, mevcut bir nesneyi kopyalayarak nesneler oluşturur. Nesnenin zaten yeniden kullanılması gereken hazırlanmış bir duruma sahip olması durumunda kullanışlıdır.

Kısacası Factory “hangi sınıfı oluşturmalıyım?” diye soruyor. Prototype ise “mevcut olan hangi nesneyi kopyalamalıyım?” diye sorar.

Prototype Pattern vs Builder Pattern

Builder Pattern karmaşık nesneleri adım adım oluşturur. Prototype Pattern, önceden hazırlanmış bir nesneyi kopyalayarak nesneler oluşturur.

İnşaat sürecinin okunabilir olması ve adım adım kontrol edilmesi gerektiğinde Builder'ı kullanın. Hazır bir nesnenin şablon olarak kullanılabildiği ve verimli bir şekilde kopyalanabildiği durumlarda Prototype'i kullanın.

Örneğin, EmailMessageBuilder birçok isteğe bağlı alan içeren bir e-posta oluştururken kullanışlıdır. Birçok fatura aynı varsayılan yapıyı paylaştığında Fatura prototipi kullanışlıdır.

Prototype Pattern vs Singleton Pattern

Singleton Pattern, bir sınıfın yalnızca bir örneğinin var olmasını sağlar. Prototype Pattern, mevcut bir nesneyi kopyalayarak birçok yeni örnek oluşturur.

Bu modeller zıt sorunları çözer. Singleton nesne oluşturmayı sınırlarken Prototype birden fazla benzer nesne oluşturmayı kolaylaştırır.

Bu farkı anlamak, geliştiricilerin karşılaştıkları nesne oluşturma sorunu için doğru modeli seçmelerine yardımcı olur.

Prototype Pattern Ne Zaman Kullanılır?

Sıfırdan bir nesne oluşturmanın pahalı, tekrarlı veya karmaşık olduğu ve mevcut bir nesneyi kopyalamanın daha kolay olduğu durumlarda Prototype Pattern'yu kullanın.

Prototype Pattern şu durumlarda faydalıdır:

  • Birçok benzer nesneye ihtiyaç vardır.

  • Nesne başlatma pahalı veya karmaşıktır.

  • Nesnelerin birçok varsayılan değeri vardır.

  • Şablonlar veya ön ayarlar kullanılır.

  • Uygulamanın tekrarlanan kurulum mantığından kaçınması gerekiyor.

  • Çalışma zamanı nesne yapılandırması kopyalanmalıdır.

  • Tam nesne sınıfı client code tarafından bilinmeyebilir.

Bu koşullar mevcutsa Prototype Pattern, nesne oluşturma işlemini daha temiz ve daha verimli hale getirebilir.

Prototype Pattern Ne Zaman Kullanılmamalı?

Nesne oluşturmanın basit ve doğrudan olduğu durumlarda Prototype Pattern kullanmayın. Bir nesnenin yalnızca birkaç değeri varsa ve pahalı bir kurulumu yoksa, bir kurucu veya fabrika kullanmak daha anlaşılır olabilir.

Aşağıdaki durumlarda Prototype Pattern'dan kaçının:

  • Nesnenin oluşturulması basittir.

  • Klonlama kafa karışıklığına veya gizli paylaşılan referanslara neden olur.

  • Nesne doğrudan kopyalanmaması gereken kaynakları içeriyor.

  • Derin kopyalama, güvenli bir şekilde yönetilemeyecek kadar karmaşıktır.

  • Bir inşaatçı veya fabrika niyetini daha açık bir şekilde ifade eder.

Prototype gerçek bir sorunu çözmelidir. Sadece klonlama mümkün olduğu için eklenmemelidir.

Prototype Pattern'nun Faydaları

Prototype Pattern, nesne yönelimli yazılım tasarımında çeşitli avantajlar sağlar.

Ana faydalar şunları içerir:

  • Mevcut hazırlanmış nesneleri kopyalayarak yeni nesneler oluşturur.

  • Tekrarlanan başlatma mantığını azaltır.

  • Kurulum pahalı olduğunda performansı artırabilir.

  • Şablon tabanlı nesne oluşturmayı destekler.

  • Birçok benzer nesne oluşturmayı kolaylaştırır.

  • Somut sınıflara bağımlılığı azaltabilir.

  • documents, şablonlar, raporlar ve oyun nesneleri için iyi çalışır.

Bu avantajlar, nesneler ortak bir başlangıç durumunu paylaştığında Prototype Pattern'yu değerli kılar.

Prototype Pattern'nun dezavantajları

Prototype Pattern'nun ana dezavantajı klonlamanın zor olabilmesidir. Nesneler iç içe geçmiş nesneler içeriyorsa sığ kopyalama, orijinalin ve klonun dahili referansları paylaşmasına neden olabilir.

Diğer bir dezavantaj ise nesnelerin dış kaynaklarla klonlanmasının tehlikeli olabilmesidir. Örneğin, database bağlantıları, dosya tanıtıcıları, yuvalar veya harici hizmet istemcileri içeren nesneler her zaman doğrudan kopyalanmamalıdır.

Prototype Pattern, geliştiricilerin klonlama davranışına aşina olmaması durumunda kodun daha az belirgin olmasını da sağlayabilir. Açık dokümantasyon ve dikkatli uygulama önemlidir.

Prototype Pattern ve Temiz Kod

Prototype Pattern, tekrarlanan kurulum mantığını ortadan kaldırdığında ve nesne oluşturmanın anlaşılmasını kolaylaştırdığında clean code'yu destekler. Uygulama birçok konfigürasyon adımını tekrarlamak yerine hazırlanmış bir prototipi kopyalayabilir.

Ancak clean code aynı zamanda netlik gerektirir. Klonlama çok fazla davranışı gizlerse veya beklenmeyen bir paylaşılan durum yaratırsa tasarımın sürdürülmesi zorlaşır.

İyi Prototype kullanımı, nesne oluşturmayı daha gizemli değil, daha basit hale getirmelidir.

Laravel'da Prototype Pattern

Laravel uygulamalarında Prototype Pattern, şablonlar, yapılandırma nesneleri, rapor nesneleri, DTOs, document oluşturma veya yeniden kullanılabilir dışa aktarma ayarlarıyla çalışırken yararlı olabilir.

Örneğin, bir raporlama modülü varsayılan bir rapor yapılandırma nesnesini tutabilir. Bir kullanıcı özel bir rapor istediğinde sistem varsayılan yapılandırmayı kopyalar ve filtreleri, tarih aralıklarını ve dışa aktarma seçeneklerini değiştirir.

Laravel ayrıca Prototype Pattern'dan farklı olan model oluşturma fabrikaları da sağlar ancak soruna bağlı olarak her ikisi de nesne oluşturmak için kullanılabilir.

Symfony'da Prototype Pattern

Symfony projelerinde Prototype Pattern, karmaşık mesaj nesneleri, komut şablonları, rapor şablonları, form yapılandırma nesneleri veya document şablonları için kullanılabilir.

Symfony hizmetleri genellikle bağımlılık ekleme kapsayıcısı tarafından yönetilir, bu nedenle geliştiricilerin hizmet nesnelerini klonlarken dikkatli olmaları gerekir. Prototype Pattern genellikle dış bağımlılıklara sahip kapsayıcı tarafından yönetilen hizmetlerden ziyade veri nesneleri, şablonlar ve yapılandırma nesneleri için daha uygundur.

Prototype Pattern ile Yapılan Yaygın Hatalar

Yaygın bir hata, sığ kopya ile derin kopya arasındaki farkı göz ardı etmektir. Bu, klonlanmış nesnelerin iç içe geçmiş nesneleri beklenmedik şekilde paylaşmasına neden olabilir.

Diğer bir hata ise database bağlantıları veya açık dosyalar gibi harici kaynaklar içeren nesnelerin klonlanmasıdır. Bu kaynaklar kopyalandığında düzgün çalışmayabilir.

Üçüncü bir hata, basit bir kurucunun daha anlaşılır olacağı durumlarda Prototype Pattern kullanmaktır. Nesne oluşturma karmaşık değilse klonlama gereksiz kafa karışıklığı yaratabilir.

Dördüncü hata ise klonlanmış nesnelerdeki benzersiz değerlerin sıfırlanmamasıdır. Örneğin, kopyalanmış faturalar, documents veya siparişler genellikle yeni kimlikler veya numaralar almalıdır.

Prototype Pattern için En İyi Uygulamalar

Prototype Pattern'yu doğru kullanmak için geliştiricilerin klonlama davranışını dikkatli bir şekilde kontrol etmesi gerekir.

Yararlı en iyi uygulamalar şunları içerir:

  • Nesnelerin pahalı veya tekrarlanan kurulumu olduğunda Prototype'i kullanın.

  • Kimlikler gibi benzersiz değerleri sıfırlamak için __clone kullanın.

  • Derin kopyalama gerektiğinde iç içe geçmiş nesneleri klonlayın.

  • Harici kaynakları doğrudan klonlamaktan kaçının.

  • Nesnenin hangi bölümlerinin kopyalandığını ve sıfırlandığını belgeleyin.

  • Şablonlar, hazır ayarlar ve yeniden kullanılabilir yapılandırmalar için prototipleri kullanın.

  • Değişikliklerin orijinali etkilememesi gerektiğinde klonlanmış nesneleri bağımsız tutun.

  • Bir inşaatçı, fabrika veya inşaatçı daha net olduğunda Prototype'i kullanmayın.

Bu uygulamalar klonlamayla ilgili hataların önlenmesine ve tasarımın anlaşılır kalmasına yardımcı olur.

Prototype Pattern Kullanmadan Önce Pratik Kontrol Listesi

Prototype Pattern'yu kullanmadan önce geliştiriciler şu soruları sorabilir:

  • Nesne oluşturma pahalı mı yoksa tekrarlı mı?

  • Birçok nesne aynı varsayılan durumu paylaşıyor mu?

  • Hazırlanan bir nesne güvenli bir şekilde kopyalanabilir mi?

  • Nesne, derin kopyalama gerektiren iç içe geçmiş nesneler içeriyor mu?

  • Klonlamadan sonra sıfırlanması gereken benzersiz alanlar var mı?

  • Nesne klonlanmaması gereken kaynaklar içeriyor mu?

  • Factory mı yoksa İnşaatçı mı bu soruna daha açık davranırdı?

Hazırlanan bir nesnenin klonlanması kodu daha temiz ve daha güvenli hale getiriyorsa Prototype Pattern iyi bir seçim olabilir.

Sonuç

Prototype Pattern, mevcut nesneleri kopyalayarak yeni nesneler oluşturan yaratıcı bir tasarım modelidir. Nesne oluşturmanın karmaşık, pahalı veya tekrarlı olduğu ve birçok nesnenin aynı varsayılan yapıyı paylaştığı durumlarda kullanışlıdır.

PHP'da, Prototype Pattern genellikle clone anahtar sözcüğü ve __clone magic yöntemi kullanılarak uygulanır. Geliştiricilerin yüzeysel kopyayı, derin kopyayı ve beklenmedik davranışları önlemek için benzersiz değerlerin nasıl sıfırlanacağını anlaması gerekir.

Prototype Pattern şablonlar, raporlar, faturalar, document oluşturma, oyun nesneleri, yapılandırma nesneleri ve yeniden kullanılabilir ön ayarlar için kullanışlıdır. Ancak nesneler iç içe geçmiş nesneler veya dış kaynaklar içerdiğinde dikkatli kullanılmalıdır. Doğru uygulandığında Prototype Pattern, nesne yönelimli yazılım projelerinde nesne oluşturmayı daha hızlı, daha temiz ve daha sürdürülebilir hale getirebilir.