Prototype Tasarım Deseni Nedir?

Sayfayı kopyala
Bu yazı Design Patterns/Tasarım Desenleri Nedir? başlıklı yazı dizisinin bir parçasıdır.
Bu içerik ağırlıklı olarak refactoring.guru sitesindeki içeriğin tercümesi ve derlenmesinden oluşturulmuştur.
Tüm tasarım desenleri ya da diğer adıyla tasarım kalıplarına yönelik ayrıntılı içeriklere yazının sonundaki bağlantılardan ulaşabilirsiniz.
💡 Özet (TL;DR):
- Amaç: Mevcut nesneleri (prototipleri), sınıflarına (concrete classes) doğrudan bağımlı kalmadan kopyalamayı (klonlamayı) sağlar.
- Kilit Yapılar: Prototip Arayüzü (
Prototype), Somut Prototipler (ConcretePrototypes) ve Prototip Havuzu (PrototypeRegistry).- Motto: Clone instead of instantiate (Sıfırdan üretmek yerine klonla). Özellikle oluşturulması maliyetli (ağır veritabanı sorguları veya API çağrıları içeren) nesneler için idealdir.
Prototype Deseninin Amacı
Prototype (Klon), bir objeyi, kodunuz onun sınıflarına bağımlı hale gelmeden kopyalamayı sağlayan oluşumsal (creational) bir tasarım desenidir.
Sorun
Bir nesneniz olduğunu ve onun birebir kopyasını oluşturmak istediğinizi düşünün. Bunu nasıl yaparsınız? Öncelikle aynı sınıftan yeni bir nesne oluşturmalı, ardından orijinal nesnenin tüm alanlarını (fields) sırayla okuyup yeni nesneye atamalısınız.
Ancak bu yöntemin iki büyük sorunu vardır:
- Gizli (Private) Alanlar: Nesnenin bazı alanları
privateolabilir ve dışarıdan doğrudan okunamaz. - Sıkı Bağımlılık (Tight Coupling): Nesneyi kopyalamak için onun somut sınıfını (concrete class) bilmek zorunda kalırsınız. Çoğu durumda istemci kod sadece arayüzü (interface) bilir, somut sınıf detaylarından habersizdir.

Çözüm
Prototip deseni kopyalama (klonlama) sürecini, kopyalanacak olan nesnenin kendisine delege etmeyi önerir. Klonlama destekleyen nesneler tek bir ortak arayüzü uygular. Bu arayüz genellikle sadece bir clone() metodu içerir.
Bu metot çalıştırıldığında, sınıf kendi içinde yeni bir nesne oluşturur ve tüm alanları (özel/private alanlar dahil) yeni nesneye kopyalar. Sınıf kendi içindeki özel alanlara erişebildiği için hiçbir veri kaybı yaşanmaz.
Klonlamaya izin veren nesnelere prototip (prototype) denir.

Gerçek Hayat Senaryosu: Web Sayfası Taslak Klonlama (Page Template Registry)
Bir içerik yönetim sisteminde (CMS) karmaşık bir sayfa taslağını veritabanından çekip sıfırdan oluşturmak yerine klonlayarak hızlıca çoğaltma örneği:
// 1. Prototip Arayüzü
interface PagePrototype {
public function clone(): PagePrototype;
}
// 2. Somut Prototip
class Page implements PagePrototype {
private string $title;
private array $widgets = [];
public function __construct(string $title) {
$this->title = $title;
// Ağır veritabanı yüklemesi veya widget kurulumu yapıldığını varsayalım
}
public function addWidget(string $widget): void {
$this->widgets[] = $widget;
}
public function setTitle(string $title): void {
$this->title = $title;
}
// Klonlama Metodu
public function clone(): PagePrototype {
$clone = new Page($this->title);
$clone->widgets = $this->widgets; // Shallow copy örneği
return $clone;
}
}
Prototype vs Factory Method vs Abstract Factory
| Desen | Üretim Yöntemi | Kalıtım (Inheritance) Kullanımı |
|---|---|---|
| Prototype | Mevcut nesneyi klonlar. | Hayır, nesneler çalışma zamanında doğrudan kopyalanır. |
| Factory Method | Nesneyi alt sınıf kurucuları ile oluşturur. | Evet, alt sınıflar kurucu metodu override eder. |
| Abstract Factory | İlişkili nesne aileleri için fabrika arayüzleri sunar. | Evet, somut fabrikalar nesne ailelerini üretir. |
Uygulanabilirlik
- Sınıf Bağımlılığını Azaltmak: Kodunuzun kopyalayacağı nesnelerin somut sınıflarından bağımsız olmasını istediğinizde kullanın.
- Alt Sınıf Enflasyonunu Engellemek: Sadece nesneleri oluşturma ve yapılandırma biçimleri farklı olan alt sınıfların sayısını azaltmak istediğinizde kullanın.
Diğer Tasarım Desenleri ile İlişkisi
- Tasarımlar genellikle Factory Method ile başlar ve esneklik ihtiyacı arttıkça Prototype desenine evrilir.
- Composite ve Decorator desenlerini yoğun kullanan tasarımlarda, karmaşık ağaç yapılarını baştan kurmak yerine klonlamak için Prototype kullanılabilir.
- Prototipler, durum (state) saklamak için kullanılan Memento desenine basit bir alternatif olabilir.
- Prototipler, Abstract Factory ve Builder gibi desenler ile birlikte Singleton olarak saklanabilir.
Prototype Tasarım Deseni Kod Örnekleri
Örnek PHP Kodu
<?php
namespace RefactoringGuru\Prototype\Conceptual;
class Prototype
{
public $primitive;
public $component;
public $circularReference;
public function __clone()
{
// DateTime nesnesini referansıyla değil, kopyalayarak aktarmak için:
$this->component = clone $this->component;
// Döngüsel referansa sahip nesneler için özel klonlama mantığı:
$this->circularReference = clone $this->circularReference;
$this->circularReference->prototype = $this;
}
}
class ComponentWithBackReference
{
public $prototype;
public function __construct(Prototype $prototype)
{
$this->prototype = $prototype;
}
}
function clientCode()
{
$p1 = new Prototype();
$p1->primitive = 245;
$p1->component = new \DateTime();
$p1->circularReference = new ComponentWithBackReference($p1);
$p2 = clone $p1;
if ($p1->primitive === $p2->primitive) {
echo "Primitive alan değerleri klona başarıyla aktarıldı.\n";
} else {
echo "Primitive alanlar kopyalanamadı.\n";
}
if ($p1->component === $p2->component) {
echo "DateTime nesnesi klonlanamadı (aynı referans).\n";
} else {
echo "DateTime nesnesi başarıyla klonlandı.\n";
}
if ($p1->circularReference === $p2->circularReference) {
echo "Döngüsel referanslı nesne klonlanamadı.\n";
} else {
echo "Döngüsel referanslı nesne başarıyla klonlandı.\n";
}
if ($p1->circularReference->prototype === $p2->circularReference->prototype) {
echo "Geriye dönük referans hala orijinal nesneyi gösteriyor.\n";
} else {
echo "Geriye dönük referans yeni klon nesnesini gösteriyor.\n";
}
}
clientCode();
Örnek Python Kodu
import copy
from typing import Any, List
class SelfReferencingEntity:
def __init__(self):
self.parent = None
def set_parent(self, parent: Any):
self.parent = parent
class SomeComponent:
def __init__(self, some_int: int, some_list_of_objects: List, some_circular_ref: Any):
self.some_int = some_int
self.some_list_of_objects = some_list_of_objects
self.some_circular_ref = some_circular_ref
def __copy__(self) -> SomeComponent:
# Sığ kopyalama (Shallow Copy)
some_list_of_objects = copy.copy(self.some_list_of_objects)
some_circular_ref = copy.copy(self.some_circular_ref)
new = self.__class__(
self.some_int, some_list_of_objects, some_circular_ref
)
new.__dict__.update(self.__dict__)
return new
def __deepcopy__(self, memo: dict = {}) -> SomeComponent:
# Derin kopyalama (Deep Copy)
some_list_of_objects = copy.deepcopy(self.some_list_of_objects, memo)
some_circular_ref = copy.deepcopy(self.some_circular_ref, memo)
new = self.__class__(
self.some_int, some_list_of_objects, some_circular_ref
)
new.__dict__ = copy.deepcopy(self.__dict__, memo)
return new
if __name__ == "__main__":
list_of_objects = [1, {1, 2, 3}, [1, 2, 3]]
circular_ref = SelfReferencingEntity()
component = SomeComponent(23, list_of_objects, circular_ref)
circular_ref.set_parent(component)
# 1. Sığ Kopyalama Testi
shallow_copied = copy.copy(component)
shallow_copied.some_list_of_objects.append("another object")
if component.some_list_of_objects[-1] == "another object":
print("Sığ Kopyalama: Referans nesneler ortak paylaşıldı (Normal davranış).")
# 2. Derin Kopyalama Testi
deep_copied = copy.deepcopy(component)
deep_copied.some_list_of_objects.append("one more object")
if component.some_list_of_objects[-1] == "one more object":
print("Derin Kopyalama başarısız.")
else:
print("Derin Kopyalama: Listeler ve içerikler tamamen bağımsızlaştırıldı.")
Sıkça Sorulan Sorular (FAQ)
Shallow Copy (Sığ Kopyalama) ile Deep Copy (Derin Kopyalama) arasındaki fark nedir?
- Shallow Copy (Sığ Kopyalama): Sadece nesnenin en üst seviye alanlarını kopyalar. Eğer nesnenin içinde başka nesne referansları varsa, klon nesne de aynı referans adreslerini gösterir. İçerideki nesnede yapılan bir değişiklik klon nesneyi de etkiler.
- Deep Copy (Derin Kopyalama): Nesnenin içindeki tüm referans nesneleri de özyinelemeli (recursive) olarak kopyalar. Klon nesne ile orijinal nesne bellek seviyesinde tamamen birbirinden bağımsız hale gelir.
Prototype Registry (Prototip Kayıt Defteri) nedir ve ne zaman kullanılmalıdır?
Eğer uygulamanızda çok sık kullanılan ve çeşitli yapılandırmalara sahip prototipleriniz varsa, bunları yönetmek için bir Prototype Registry (Prototip Havuzu / Kayıt Defteri) oluşturabilirsiniz. Bu havuz genellikle anahtar-değer (key-value) şeklinde çalışan bir cache yapısıdır (örn: Map). İstemciler Registry.get("RedButtonPrototype") gibi çağrılarla önceden yapılandırılmış prototiplere kolayca erişip onları klonlayabilir.
JavaScript/TypeScript'teki prototype yapısı bu desenle mi ilgilidir?
Evet, doğrudan ilgilidir. JavaScript sınıf tabanlı (class-based) kalıtım yerine prototip tabanlı (prototypal inheritance) bir kalıtım modeli kullanır. JavaScript'te her nesnenin gizli bir [[Prototype]] referansı vardır. Bir nesnenin bir özelliğine erişilmek istendiğinde, özellik nesnede yoksa otomatik olarak prototip nesnesine sorulur. Bu, Prototype deseninin dil mimarisi düzeyinde uygulanmış halidir.
Diğer Tasarım Kalıpları/Design Patterns
Oluşumsal Kalıplar (Creational Patterns)
Factory Method, Abstract Factory, Builder, Prototype, Singleton
Yapısal Kalıplar (Structural Patterns)
Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
Davranışsal Kalıplar (Behavioral Patterns)
Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor
