Παραγόμενα εργοστάσια
Το Nette DI μπορεί να παράγει αυτόματα κώδικα εργοστασίων με βάση τη διεπαφή, γεγονός που σας απαλλάσσει από τη συγγραφή κώδικα.
Ένα εργοστάσιο είναι μια κλάση που δημιουργεί και διαμορφώνει αντικείμενα. Επομένως, τους μεταβιβάζει και τις εξαρτήσεις τους. Μην συγχέετε με το πρότυπο σχεδίασης factory method, το οποίο περιγράφει έναν συγκεκριμένο τρόπο χρήσης των εργοστασίων και δεν σχετίζεται με αυτό το θέμα.
Έχουμε δείξει πώς μοιάζει ένα τέτοιο εργοστάσιο στο εισαγωγικό κεφάλαιο:
class ArticleFactory
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
public function create(): Article
{
return new Article($this->db);
}
}
Το Nette DI μπορεί να παράγει αυτόματα κώδικα εργοστασίου. Το μόνο που
έχετε να κάνετε είναι να δημιουργήσετε μια διεπαφή και το Nette DI θα
δημιουργήσει μια υλοποίηση. Η διεπαφή πρέπει να έχει ακριβώς μία
μέθοδο με το όνομα create
και να δηλώνει έναν τύπο επιστροφής:
interface ArticleFactory
{
function create(): Article;
}
Έτσι, το εργοστάσιο ArticleFactory
έχει μια μέθοδο create
που
δημιουργεί αντικείμενα Article
. Η κλάση Article
μπορεί να
μοιάζει, για παράδειγμα, με την ακόλουθη:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
}
Προσθέστε το εργοστάσιο στο αρχείο ρυθμίσεων:
services:
- ArticleFactory
Το Nette DI θα δημιουργήσει την αντίστοιχη υλοποίηση του εργοστασίου.
Έτσι, στον κώδικα που χρησιμοποιεί το εργοστάσιο, ζητάμε το αντικείμενο μέσω διεπαφής και το Nette DI χρησιμοποιεί την υλοποίηση που παράγεται:
class UserController
{
public function __construct(
private ArticleFactory $articleFactory,
) {
}
public function foo()
{
// αφήστε το εργοστάσιο να δημιουργήσει ένα αντικείμενο
$article = $this->articleFactory->create();
}
}
Παραμετροποιημένο εργοστάσιο
Η μέθοδος του εργοστασίου create
μπορεί να δέχεται παραμέτρους
τις οποίες στη συνέχεια μεταβιβάζει στον κατασκευαστή. Για παράδειγμα,
ας προσθέσουμε ένα αναγνωριστικό συγγραφέα άρθρου στην κλάση
Article
:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
private int $authorId,
) {
}
}
Θα προσθέσουμε επίσης την παράμετρο στο εργοστάσιο:
interface ArticleFactory
{
function create(int $authorId): Article;
}
Επειδή η παράμετρος στον κατασκευαστή και η παράμετρος στο εργοστάσιο έχουν το ίδιο όνομα, η Nette DI θα τις περάσει αυτόματα.
Προηγμένος ορισμός
Ο ορισμός μπορεί επίσης να γραφτεί σε μορφή πολλαπλών γραμμών
χρησιμοποιώντας το πλήκτρο implement
:
services:
articleFactory:
implement: ArticleFactory
Όταν γράφετε με αυτόν τον μακρύτερο τρόπο, είναι δυνατόν να παρέχετε
πρόσθετα ορίσματα για τον κατασκευαστή στο κλειδί arguments
και
πρόσθετες ρυθμίσεις χρησιμοποιώντας το setup
, όπως ακριβώς και
για τις κανονικές υπηρεσίες.
Παράδειγμα: αν η μέθοδος create()
δεν δεχόταν την παράμετρο
$authorId
, θα μπορούσαμε να καθορίσουμε μια σταθερή τιμή στη
διαμόρφωση που θα περνούσε στον κατασκευαστή Article
:
services:
articleFactory:
implement: ArticleFactory
arguments:
authorId: 123
Ή, αντίθετα, αν η create()
δεχόταν την παράμετρο $authorId
αλλά
δεν αποτελούσε μέρος του κατασκευαστή και περνούσε από τη μέθοδο
Article::setAuthorId()
, θα αναφερόμασταν σε αυτήν στην ενότητα
setup
:
services:
articleFactory:
implement: ArticleFactory
setup:
- setAuthorId($authorId)
Accessor
Εκτός από τα εργοστάσια, η Nette μπορεί επίσης να δημιουργήσει τους
λεγόμενους accessors. Ο accessor είναι ένα αντικείμενο με τη μέθοδο get()
που επιστρέφει μια συγκεκριμένη υπηρεσία από το δοχείο DI. Πολλαπλές
κλήσεις get()
θα επιστρέφουν πάντα το ίδιο παράδειγμα.
Οι accessors φέρνουν lazy-loading στις εξαρτήσεις. Ας έχουμε μια κλάση που
καταγράφει σφάλματα σε μια ειδική βάση δεδομένων. Αν η σύνδεση της
βάσης δεδομένων περνούσε ως εξάρτηση στον κατασκευαστή της, η σύνδεση
θα έπρεπε να δημιουργείται πάντα, αν και θα χρησιμοποιούνταν μόνο
σπάνια όταν εμφανίζεται ένα σφάλμα, οπότε η σύνδεση θα έμενε ως επί το
πλείστον αχρησιμοποίητη. Αντ' αυτού, η κλάση μπορεί να περάσει έναν
accessor και όταν καλείται η μέθοδος get()
, μόνο τότε δημιουργείται το
αντικείμενο της βάσης δεδομένων:
Πώς να δημιουργήσετε έναν προσπελάτη; Γράψτε μόνο μια διεπαφή και η
Nette DI θα δημιουργήσει την υλοποίηση. Η διασύνδεση πρέπει να έχει
ακριβώς μία μέθοδο που ονομάζεται get
και πρέπει να δηλώνει τον
τύπο επιστροφής:
interface PDOAccessor
{
function get(): PDO;
}
Προσθέστε την προσπέλαση στο αρχείο ρυθμίσεων μαζί με τον ορισμό της υπηρεσίας που θα επιστρέψει η προσπέλαση:
services:
- PDOAccessor
- PDO(%dsn%, %user%, %password%)
Ο accessor επιστρέφει μια υπηρεσία τύπου PDO
και επειδή υπάρχει
μόνο μια τέτοια υπηρεσία στη διαμόρφωση, ο accessor θα την επιστρέψει. Με
πολλαπλές διαμορφωμένες υπηρεσίες αυτού του τύπου μπορείτε να
καθορίσετε ποια θα επιστραφεί χρησιμοποιώντας το όνομά της, για
παράδειγμα - PDOAccessor(@db1)
.
Multifactory/Accessor
Μέχρι στιγμής, τα εργοστάσια και οι accessors μπορούσαν να δημιουργήσουν
ή να επιστρέψουν μόνο ένα αντικείμενο. Μπορεί επίσης να δημιουργηθεί
ένα multifactory σε συνδυασμό με έναν accessor. Η διεπαφή μιας τέτοιας κλάσης
multifactory μπορεί να αποτελείται από πολλαπλές μεθόδους που ονομάζονται
create<name>()
και get<name>()
, για παράδειγμα:
interface MultiFactory
{
function createArticle(): Article;
function getDb(): PDO;
}
Αντί να περνάτε πολλαπλά παραγόμενα εργοστάσια και accessors, μπορείτε να περνάτε μόνο ένα σύνθετο multifactory.
Εναλλακτικά, μπορείτε να χρησιμοποιήσετε το get()
με μια
παράμετρο αντί για πολλαπλές μεθόδους:
interface MultiFactoryAlt
{
function get($name): PDO;
}
Σε αυτή την περίπτωση, το MultiFactory::getArticle()
κάνει το ίδιο πράγμα
με το MultiFactoryAlt::get('article')
. Ωστόσο, η εναλλακτική σύνταξη έχει
μερικά μειονεκτήματα. Δεν είναι σαφές ποιες τιμές $name
υποστηρίζονται και ο τύπος επιστροφής δεν μπορεί να καθοριστεί στη
διεπαφή όταν χρησιμοποιούνται πολλές διαφορετικές τιμές $name
.
Ορισμός με λίστα
Αυτός ο τρόπος μπορεί να χρησιμοποιηθεί για τον ορισμό ενός πολλαπλού εργοστασίου στη διαμόρφωση:
services:
- MultiFactory(
article: Article # defines createArticle()
db: PDO(%dsn%, %user%, %password%) # defines getDb()
)
Ή, στον ορισμό του εργοστασίου, μπορούμε να αναφερθούμε σε υπάρχουσες υπηρεσίες χρησιμοποιώντας μια αναφορά:
services:
article: Article
- PDO(%dsn%, %user%, %password%)
- MultiFactory(
article: @article # defines createArticle()
db: @\PDO # defines getDb()
)
Ορισμός με ετικέτες
Μια άλλη επιλογή για τον ορισμό ενός multifactory είναι η χρήση ετικετών:
services:
- App\Core\RouterFactory::createRouter
- App\Model\DatabaseAccessor(
db1: @database.db1.explorer
)