Factory Method 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ç: Nesne oluşturma mantığını (
newkullanımını) doğrudan istemci koddan alıp soyut bir metoda devrederek, istemcinin somut sınıflara bağımlı olmasını önler.- Kilit Yapılar: Ürün Arayüzü (
Product), Somut Ürünler (ConcreteProduct), Oluşturucu (Creator) ve Somut Oluşturucular (ConcreteCreator).- En Büyük Avantajı: Kodumuza yeni ürün tipleri veya nesneler eklerken mevcut istemci kodunu bozmaz veya değiştirmeyiz (Open/Closed Principle).
Factory Method Tasarım Deseni Amacı
Kelime anlamı "Fabrika Metodu" olan Factory Method, üst sınıfta nesneler oluşturmak için bir arabirim sağlayan, ancak alt sınıfların oluşturulacak bu nesne türünü değiştirmesine izin veren bir yaratımsal (creational) tasarım desenidir.
Sorun
Bir lojistik yönetim uygulaması oluşturduğunuzu hayal edin. Uygulamanızın ilk sürümü sadece kamyon nakliyesini desteklediğinden, kodunuzun neredeyse tamamını Kamyon sınıfına bağımlı olacak şekilde yazdınız.
Bir süre sonra uygulamanız çok popüler hale geldi ve deniz taşımacılığı şirketleri sisteme deniz taşımacılığını da eklemeniz için ısrarla sizi aramaya başladı.

Geliştirme aşamasında bu harika bir haber olsa da kod tasarımı açısından büyük bir problemdir. Çünkü şu an kodunuzun büyük bölümü Kamyon sınıfı ile sıkı sıkıya entegre durumdadır. Gemi sınıfını eklemek, tüm kod tabanında değişiklik yapmayı ve nakliye aracının tipine göre uygulamanın davranışlarını değiştiren karmaşık if-else / switch kontrol yapıları eklemeyi gerektirir. Yarın bir gün havayolu taşımacılığı da eklemek istediğinizde aynı acılı süreci en baştan yaşamak zorunda kalırsınız.
Çözüm
Factory Method deseni, nesneleri doğrudan istemci kodunda new operatörü ile üretmek yerine, bu oluşturma çağrılarını özel bir "fabrika metodu" (factory method) içerisine taşımayı önerir. Nesneler yine new ile oluşturulur ancak bu işlem fabrika metodunun sorumluluğundadır. Bu metottan dönen nesneler genellikle "Ürün" (Product) olarak adlandırılır.

Bu değişikliğin en büyük gücü şudur: Artık bir üst sınıfı genişleterek, onun oluşturacağı nesnenin (ürünün) türünü alt sınıflarda ezerek (override) dinamik olarak değiştirebiliriz.
Ancak burada önemli bir kural vardır: Alt sınıfların farklı ürünler döndürebilmesi için bu ürünlerin ortak bir arayüzü (Interface) veya soyut sınıfı (Abstract Class) paylaşması gerekir. Ayrıca üst sınıftaki fabrika metodunun dönüş tipi bu ortak arayüz olmalıdır.

Örneğin hem Kamyon hem de Gemi sınıfları tasi() adında bir metot içeren Transport arayüzünü uygulamalıdır. Kamyonlar karadan, gemiler ise denizden taşır. KaraLojistigi sınıfı kamyon nesnesi dönerken, DenizLojistigi sınıfı gemi nesnesi döner.

İstemci kod (client code) bu iki nesne arasındaki farkı bilmek zorunda değildir. İstemci sadece bir Transport nesnesi aldığını ve bunun tasi() metoduna sahip olduğunu bilir. Taşıma işleminin arka planda kara yoluyla mı yoksa deniz yoluyla mı yapıldı istemcinin odağı dışındadır.
Gerçek Hayat Senaryosu: Bildirim Gönderim Sistemi
Yazılım projelerinde çok sık kullanılan "Bildirim Gönderimi (Notification Service)" mimarisi üzerinde bu deseni kurgulayalım:
// 1. Ürün Arayüzü (Product Interface)
interface Notification {
public function send(string $message): void;
}
// 2. Somut Ürünler (Concrete Products)
class EmailNotification implements Notification {
public function send(string $message): void {
echo "E-posta gönderildi: '$message'\n";
}
}
class SmsNotification implements Notification {
public function send(string $message): void {
echo "SMS gönderildi: '$message'\n";
}
}
// 3. Oluşturucu Sınıf (Creator)
abstract class NotificationCreator {
// Fabrika Metodu (Factory Method)
abstract public function createNotification(): Notification;
// İş mantığı ürüne bağımlı olmadan çalışır
public function notify(string $message): void {
$notification = $this->createNotification();
$notification->send($message);
}
}
// 4. Somut Oluşturucular (Concrete Creators)
class EmailNotificationCreator extends NotificationCreator {
public function createNotification(): Notification {
return new EmailNotification();
}
}
class SmsNotificationCreator extends NotificationCreator {
public function createNotification(): Notification {
return new SmsNotification();
}
}
Factory Method vs Abstract Factory
| Kriter | Factory Method | Abstract Factory |
|---|---|---|
| Odağı | Tek bir ürün nesnesinin oluşturulması ve alt sınıfa devri. | Birbiriyle ilişkili veya bağımlı ürün ailelerinin oluşturulması. |
| Uygulama | Kalıtım (Inheritance) tabanlıdır. | Kompozisyon (Composition) tabanlıdır. |
| Genişletilebilirlik | Yeni bir ürün eklemek için sadece yeni bir alt sınıf yazılır. | Yeni bir ürün ailesi eklemek tüm fabrika arayüzünün güncellenmesini gerektirebilir. |
Uygulanabilirlik
- Bilinmeyen Nesne Tipleri: Kodunuzun çalışması gereken nesnelerin türlerini ve bağımlılıklarını önceden tam olarak kestiremediğinizde kullanın.
- Bileşen Genişletme: Kütüphanenizin veya framework'ünüzün kullanıcılarının, iç bileşenleri alt sınıflar aracılığıyla kolayca genişletebilmesini istediğinizde kullanın.
- Nesne Havuzlama (Object Pooling): Sistem kaynaklarından tasarruf etmek amacıyla, her seferinde yeni bir nesne oluşturmak yerine mevcut nesneleri (veritabanı bağlantıları, soketler vb.) tekrar kullanmak istediğinizde kullanın.
Diğer Tasarım Desenleri ile İlişkisi
- Çoğu tasarım Factory Method kullanılarak başlar ve zamanla ihtiyaçlar arttıkça Abstract Factory, Prototype veya Builder yapılarına evrilir.
- Abstract Factory sınıfları genellikle birden fazla Factory Method içerir.
- Factory Method, Template Method tasarım deseninin özel bir adımı olarak sıklıkla kullanılır.
Factory Method Kod Örnekleri
Örnek PHP Kodu
<?php
namespace RefactoringGuru\FactoryMethod\Conceptual;
abstract class Creator
{
abstract public function factoryMethod(): Product;
public function someOperation(): string
{
$product = $this->factoryMethod();
$result = "Creator: Aynı oluşturucu kodu şununla çalıştı: " .
$product->operation();
return $result;
}
}
class ConcreteCreator1 extends Creator
{
public function factoryMethod(): Product
{
return new ConcreteProduct1();
}
}
class ConcreteCreator2 extends Creator
{
public function factoryMethod(): Product
{
return new ConcreteProduct2();
}
}
interface Product
{
public function operation(): string;
}
class ConcreteProduct1 implements Product
{
public function operation(): string
{
return "{ConcreteProduct1 Sonucu}";
}
}
class ConcreteProduct2 implements Product
{
public function operation(): string
{
return "{ConcreteProduct2 Sonucu}";
}
}
function clientCode(Creator $creator)
{
echo "İstemci: Oluşturucu sınıfından haberim yok, fakat yine de sorunsuz çalışıyor.\n"
. $creator->someOperation();
}
echo "Uygulama: ConcreteCreator1 ile çalışıyor.\n";
clientCode(new ConcreteCreator1());
echo "\n\n";
echo "Uygulama: ConcreteCreator2 ile çalışıyor.\n";
clientCode(new ConcreteCreator2());
Örnek Python Kodu
from __future__ import annotations
from abc import ABC, abstractmethod
class Creator(ABC):
@abstractmethod
def factory_method(self) -> Product:
pass
def some_operation(self) -> str:
product = self.factory_method()
result = f"Creator: Aynı oluşturucu kodu şununla çalıştı: {product.operation()}"
return result
class ConcreteCreator1(Creator):
def factory_method(self) -> Product:
return ConcreteProduct1()
class ConcreteCreator2(Creator):
def factory_method(self) -> Product:
return ConcreteProduct2()
class Product(ABC):
@abstractmethod
def operation(self) -> str:
pass
class ConcreteProduct1(Product):
def operation(self) -> str:
return "{ConcreteProduct1 Sonucu}"
class ConcreteProduct2(Product):
def operation(self) -> str:
return "{ConcreteProduct2 Sonucu}"
def client_code(creator: Creator) -> None:
print(f"İstemci: Oluşturucu sınıfından bağımsız çalışıyorum.\n"
f"{creator.some_operation()}", end="")
if __name__ == "__main__":
print("Uygulama: ConcreteCreator1 ile başlatıldı.")
client_code(ConcreteCreator1())
print("\n")
print("Uygulama: ConcreteCreator2 ile başlatıldı.")
client_code(ConcreteCreator2())
Sıkça Sorulan Sorular (FAQ)
Basit Fabrika (Simple Factory) ile Fabrika Metodu (Factory Method) arasındaki fark nedir?
- Simple Factory: Genellikle tek bir sınıftır ve parametreye göre (örn:
"SMS"veya"EMAIL")switch-casekullanarak nesne üretip döner. Polymorphism ve inheritance içermez. - Factory Method: Nesne üretme işini doğrudan bir sınıfa değil, kalıtım (inheritance) yoluyla alt sınıflara bırakır. Her ürün için ayrı bir oluşturucu sınıf (
SmsNotificationCreatorgibi) bulunur ve yeni bir ürün eklenirken mevcut sınıflara dokunulmaz.
Neden Constructor (Yapıcı Metot) yerine nesne havuzlarında Factory Method kullanılır?
Nesne yönelimli dillerde yapıcı metotlar (__construct(), __init__() vb.) tanım gereği her çağrıldığında bellekte yeni bir nesne oluşturmak zorundadır. Ancak veritabanı bağlantısı gibi ağır nesneleri tekrar kullanmak (pooling) istediğimizde yapıcı metotlar işe yaramaz. Fabrika metotları ise normal metotlar oldukları için statik bir dizi veya havuzdan daha önce oluşturulmuş boşta duran eski bir nesneyi döndürebilirler.
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
