Üretilen Factory'ler
Nette DI, arayüzlere dayanarak factory kodunu otomatik üretebilir ve sizi kod yazmaktan kurtarır.
Factory, nesneleri oluşturmaktan ve bağımlılıklarını aktarmaktan sorumlu bir sınıftır. Lütfen bunu, factory kullanımının belirli bir biçimini anlatan ve bu konuyla ilgisi olmayan factory method tasarım deseniyle karıştırmayın.
Böyle bir factory'nin nasıl göründüğünü giriş bölümünde gösterdik:
class ArticleFactory
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
public function create(): Article
{
return new Article($this->db);
}
}
Nette DI factory kodunu otomatik üretebilir. Tek yapmanız gereken bir arayüz oluşturmaktır; gerçekleştirimi Nette DI
üretir. Arayüzde create adında tam olarak bir metot bulunmalı ve bir dönüş türü bildirilmelidir:
interface ArticleFactory
{
function create(): Article;
}
Yani ArticleFactory factory'sinin, Article nesneleri oluşturan bir create metodu
vardır. Article sınıfı örneğin şöyle görünebilir:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
}
Factory'yi yapılandırma dosyasına ekleyin:
services:
- ArticleFactory
Nette DI ilgili factory gerçekleştirimini üretir.
Factory'yi kullanan kodda nesneyi arayüzü üzerinden isteyin; Nette DI üretilen gerçekleştirimi verir:
class UserController
{
public function __construct(
private ArticleFactory $articleFactory,
) {
}
public function foo()
{
// bırakın nesneyi factory oluştursun
$article = $this->articleFactory->create();
}
}
Parametreli Factory
create factory metodu, sonra yapıcıya aktardığı parametreler alabilir. Örneğin Article
sınıfına makale yazarının ID'sini ekleyelim:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
private int $authorId,
) {
}
}
Parametreyi factory'ye de ekleyeceğiz:
interface ArticleFactory
{
function create(int $authorId): Article;
}
Yapıcıdaki parametre adı ($authorId) factory metodundaki parametre adıyla eşleştiğinden, Nette DI onu
otomatik olarak aktarır.
Gelişmiş Tanım
Tanım, implement anahtarı kullanılarak çok satırlı biçimde de yazılabilir:
services:
articleFactory:
implement: ArticleFactory
Bu uzun biçimi kullanmak, sıradan servis tanımlarında olduğu gibi arguments anahtarıyla yapıcı için ek
argümanlar belirtmeyi ve setup ile daha fazla yapılandırma yapmayı sağlar.
Örnek: create() metodu $authorId parametresini almasaydı, Article yapıcısına
aktarılacak sabit bir değeri yapılandırmada verebilirdik:
services:
articleFactory:
implement: ArticleFactory
arguments:
authorId: 123
Tersine, create() metodu $authorId parametresini alsaydı ama bu parametre yapıcının bir parçası
olmayıp Article::setAuthorId() gibi bir metotla aktarılsaydı, parametreye setup bölümünde
başvururduk:
services:
articleFactory:
implement: ArticleFactory
setup:
- setAuthorId($authorId)
Accessor
Nette, factory'lerin yanı sıra accessor denen nesneleri de üretebilir. Bunlar, DI container'dan belirli bir servisi
döndüren bir get() metoduna sahip nesnelerdir. get() metodunun yinelenen çağrıları her zaman aynı
örneği döndürür.
Accessor'lar, bağımlılıklar için tembel yükleme sağlar. Hataları özel bir veritabanına günlükleyen bir sınıf
düşünün. Bu sınıf veritabanı bağlantısını yapıcı bağımlılık enjeksiyonuyla alsaydı, hatalar ender oluşsa ve
bağlantı çoğu zaman kullanılmasa bile bağlantı her zaman kurulurdu. Bunun yerine sınıf bir accessor alabilir. Veritabanı
nesnesi (bağlantı) yalnızca accessor'ın get() metodu ilk kez çağrıldığında oluşturulur.
Accessor nasıl oluşturulur? Yalnızca bir arayüz yazın; gerçekleştirimi Nette DI üretir. Arayüzde get
adında, parametre almayan ve dönüş türü bildiren tam olarak bir metot bulunmalıdır:
interface PDOAccessor
{
function get(): PDO;
}
Accessor'ı, döndürmesi gereken servisin tanımıyla birlikte yapılandırma dosyasına ekleyin:
services:
- PDOAccessor
- PDO(%dsn%, %user%, %password%)
Accessor bir PDO servisi döndürdüğünden ve yapılandırmada bu türden yalnızca bir servis tanımlı
olduğundan, accessor o servisi döndürür. Bu türden birden çok servis varsa, accessor'ın hangisini döndüreceğini adıyla
belirtin, örneğin - PDOAccessor(@db1).
Çoklu Factory/Accessor
Şimdiye kadar factory'lerimiz ve accessor'larımız yalnızca tek bir nesne türünü oluşturabiliyor ya da
döndürebiliyordu. Ancak factory ve accessor özelliklerini birleştiren çoklu factory'ler de kolayca oluşturabilirsiniz.
Böyle bir bileşenin arayüzü create<Name>() ve get<Name>() adlı birden çok metot
içerebilir, örneğin:
interface MultiFactory
{
function createArticle(): Article;
function getDb(): PDO;
}
Yani birden çok ayrı factory ve accessor enjekte etmek yerine tek ve daha kapsamlı bir bileşen enjekte edebilirsiniz.
Alternatif olarak, birden çok metot yerine parametreli bir get() kullanılabilir:
interface MultiFactoryAlt
{
function get($name): PDO;
}
O zaman MultiFactory::getDb(), MultiFactoryAlt::get('db') ile aynı işi yapar. Ancak bu alternatif
yazımın sakıncası, $name için desteklenen değerlerin arayüz imzasından açıkça anlaşılmamasıdır.
Ayrıca arayüzde farklı $name değerleri için farklı dönüş türleri tanımlayamazsınız.
Arayüz, get($name) yerine her çağrıda yeni bir örnek döndüren create($name) metodunu da
bildirebilir (oysa get() paylaşılan bir örnek döndürür). Arayüzde böyle parametreli yalnızca bir metot
bulunabilir. Metodun dönüş türü nullable ise (örneğin ?PDO), bilinmeyen bir $name değeri için
istisna fırlatmak yerine null döndürür.
Listeyle Tanım
Çoklu factory'yi yapılandırmada bir liste kullanarak, servisleri satır içinde yazarak tanımlayabilirsiniz:
services:
- MultiFactory(
article: Article() # createArticle() tanımlar
db: PDO(%dsn%, %user%, %password%) # getDb() tanımlar
)
Alternatif olarak, çoklu factory tanımında var olan servislere referanslarla başvurabilirsiniz:
services:
article: Article
- PDO(%dsn%, %user%, %password%)
- MultiFactory(
article: @article # createArticle() tanımlar
db: @\PDO # getDb() tanımlar
)
Etiketlerle Tanım
Çoklu factory tanımlamanın bir başka yolu da etiketleri kullanmaktır. Etiketin değeri, ilgili metodun adını belirler:
services:
article:
create: Article
tags: {multi: article} # createArticle() tanımlar
db:
create: PDO(%dsn%, %user%, %password%)
tags: {multi: db} # getDb() tanımlar
- MultiFactory(tagged: multi)