Üretilmiş Fabrikalar
Nette DI, arayüzlere dayalı olarak fabrika kodunu otomatik olarak üretebilir, bu da size kod yazmaktan tasarruf sağlar.
Fabrika, nesneleri üreten ve yapılandıran bir sınıftır. Dolayısıyla onlara bağımlılıklarını da aktarır. Lütfen bunu, fabrikaların belirli bir kullanım şeklini açıklayan ve bu konuyla ilgisi olmayan factory method tasarım deseniyle karıştırmayın.
Böyle bir fabrikanın nasıl göründüğünü giriş bölümü içinde gösterdik:
class ArticleFactory
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
public function create(): Article
{
return new Article($this->db);
}
}
Nette DI, fabrika kodunu otomatik olarak üretebilir. Tek yapmanız gereken bir arayüz oluşturmaktır ve Nette DI uygulamayı
üretecektir. Arayüzün tam olarak create
adında bir metodu olmalı ve dönüş değeri tipini bildirmelidir:
interface ArticleFactory
{
function create(): Article;
}
Yani ArticleFactory
fabrikasının, 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,
) {
}
}
Fabrikayı yapılandırma dosyasına ekleriz:
services:
- ArticleFactory
Nette DI, fabrikanın ilgili uygulamasını üretecektir.
Fabrikayı kullanan kodda, nesneyi arayüze göre talep ederiz ve Nette DI üretilen uygulamayı kullanır:
class UserController
{
public function __construct(
private ArticleFactory $articleFactory,
) {
}
public function foo()
{
// fabrikanın nesneyi oluşturmasına izin veririz
$article = $this->articleFactory->create();
}
}
Parametreli Fabrika
Fabrika metodu create
, daha sonra kurucuya aktarılacak parametreleri kabul edebilir. Ö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 fabrikaya da ekleriz:
interface ArticleFactory
{
function create(int $authorId): Article;
}
Kurucudaki parametre ile fabrikadaki parametrenin aynı adı taşıması sayesinde, Nette DI bunları tamamen otomatik olarak aktarır.
Gelişmiş Tanım
Tanım, implement
anahtarını kullanarak çok satırlı bir formda da yazılabilir:
services:
articleFactory:
implement: ArticleFactory
Bu daha uzun yolla yazarken, normal servislerde olduğu gibi arguments
anahtarında kurucu için ek argümanlar ve
setup
kullanarak ek yapılandırma belirtmek mümkündür.
Örnek: Eğer create()
metodu $authorId
parametresini kabul etmeseydi, yapılandırmada
Article
kurucusuna aktarılacak sabit bir değer belirtebilirdik:
services:
articleFactory:
implement: ArticleFactory
arguments:
authorId: 123
Veya tam tersi, eğer create()
$authorId
parametresini kabul etseydi, ancak kurucunun bir parçası
olmasaydı ve Article::setAuthorId()
metoduyla aktarılsaydı, ona setup
bölümünde başvururduk:
services:
articleFactory:
implement: ArticleFactory
setup:
- setAuthorId($authorId)
Erişimci (Accessor)
Nette, fabrikaların yanı sıra erişimciler (accessor) olarak adlandırılanları da üretebilir. Bunlar, DI konteynerinden
belirli bir servisi döndüren bir get()
metoduna sahip nesnelerdir. get()
'in tekrarlanan çağrıları
her zaman aynı örneği döndürür.
Erişimciler, bağımlılıklara tembel yükleme (lazy-loading) sağlar. Hataları özel bir veritabanına yazan bir
sınıfımız olduğunu varsayalım. Eğer bu sınıf veritabanı bağlantısını kurucu bağımlılığı olarak alsaydı,
bağlantı her zaman oluşturulmak zorunda kalırdı, ancak pratikte hata yalnızca istisnai olarak ortaya çıkar ve bu nedenle
çoğu zaman bağlantı kullanılmadan kalırdı. Bunun yerine, sınıf bir erişimci aktarır ve yalnızca onun
get()
metodu çağrıldığında veritabanı nesnesi oluşturulur:
Bir erişimci nasıl oluşturulur? Sadece bir arayüz yazmanız yeterlidir ve Nette DI uygulamayı üretecektir. Arayüzün tam
olarak get
adında bir metodu olmalı ve dönüş değeri tipini bildirmelidir:
interface PDOAccessor
{
function get(): PDO;
}
Erişimciyi, döndüreceği servisin tanımının da bulunduğu yapılandırma dosyasına ekleriz:
services:
- PDOAccessor
- PDO(%dsn%, %user%, %password%)
Erişimci PDO
tipinde bir servis döndürdüğü ve yapılandırmada bu türden tek bir servis olduğu için, tam
olarak onu döndürecektir. Eğer ilgili tipten birden fazla servis olsaydı, döndürülen servisi adıyla belirlerdik, örn.
- PDOAccessor(@db1)
.
Çoklu Fabrika/Erişimci
Fabrikalarımız ve erişimcilerimiz şimdiye kadar her zaman yalnızca bir nesne üretebildi veya döndürebildi. Ancak,
erişimcilerle birleştirilmiş çoklu fabrikaları da çok kolay bir şekilde oluşturmak mümkündür. Böyle bir sınıfın
arayüzü, create<name>()
ve get<name>()
adlarına sahip istenilen sayıda metot
içerecektir, örn.:
interface MultiFactory
{
function createArticle(): Article;
function getDb(): PDO;
}
Yani, birkaç üretilmiş fabrika ve erişimci aktarmak yerine, daha fazlasını yapabilen daha karmaşık bir fabrika aktarırız.
Alternatif olarak, birkaç metot yerine parametreli get()
kullanılabilir:
interface MultiFactoryAlt
{
function get($name): PDO;
}
O zaman MultiFactory::getArticle()
'ın MultiFactoryAlt::get('article')
ile aynı şeyi yaptığı
geçerlidir. Ancak, alternatif yazımın dezavantajı, hangi $name
değerlerinin desteklendiğinin açık olmaması
ve mantıksal olarak arayüzde farklı $name
için farklı dönüş değerlerini ayırt etmenin mümkün
olmamasıdır.
Liste ile Tanım
Bu şekilde, yapılandırmada çoklu bir fabrika tanımlanabilir:
services:
- MultiFactory(
article: Article # createArticle() tanımlar
db: PDO(%dsn%, %user%, %password%) # getDb() tanımlar
)
Veya fabrika tanımında mevcut servislere referansla başvurabiliriz:
services:
article: Article
- PDO(%dsn%, %user%, %password%)
- MultiFactory(
article: @article # createArticle() tanımlar
db: @\PDO # getDb() tanımlar
)
Etiketlerle Tanım
İkinci seçenek, tanım için etiketleri kullanmaktır:
services:
- App\Core\RouterFactory::createRouter
- App\Model\DatabaseAccessor(
db1: @database.db1.explorer
)