Τι είναι το Dependency Injection;

Αυτό το κεφάλαιο σας εισάγει στις βασικές πρακτικές προγραμματισμού που πρέπει να ακολουθείτε κατά τη συγγραφή οποιασδήποτε εφαρμογής. Αυτά είναι τα βασικά που απαιτούνται για τη συγγραφή καθαρού, κατανοητού και συντηρήσιμου κώδικα.

Αν μάθετε και ακολουθήσετε αυτούς τους κανόνες, η Nette θα είναι δίπλα σας σε κάθε σας βήμα. Θα χειρίζεται τις εργασίες ρουτίνας για εσάς και θα σας κάνει να αισθάνεστε όσο το δυνατόν πιο άνετα, ώστε να μπορείτε να επικεντρωθείτε στην ίδια τη λογική.

Οι αρχές που θα παρουσιάσουμε εδώ είναι αρκετά απλές. Δεν χρειάζεται να ανησυχείτε για τίποτα.

Θυμάστε το πρώτο σας πρόγραμμα;

Δεν έχουμε ιδέα σε ποια γλώσσα το γράψατε, αλλά αν ήταν PHP, πιθανόν να έμοιαζε κάπως έτσι:

function addition(float $a, float $b): float
{
	return $a + $b;
}

echo addition(23, 1); // εκτυπώσεις 24

Λίγες ασήμαντες γραμμές κώδικα, αλλά τόσες πολλές βασικές έννοιες κρυμμένες σε αυτές. Ότι υπάρχουν μεταβλητές. Ότι ο κώδικας αναλύεται σε μικρότερες μονάδες, οι οποίες είναι συναρτήσεις, για παράδειγμα. Ότι τους δίνουμε ορίσματα εισόδου και επιστρέφουν αποτελέσματα. Το μόνο που λείπει είναι οι συνθήκες και οι βρόχοι.

Το γεγονός ότι δίνουμε είσοδο σε μια συνάρτηση και αυτή επιστρέφει ένα αποτέλεσμα είναι μια απολύτως κατανοητή έννοια που χρησιμοποιείται και σε άλλα πεδία, όπως τα μαθηματικά.

Μια συνάρτηση έχει μια υπογραφή, η οποία αποτελείται από το όνομά της, μια λίστα παραμέτρων και τους τύπους τους και, τέλος, τον τύπο της τιμής επιστροφής. Ως χρήστες, μας ενδιαφέρει η υπογραφή- συνήθως δεν χρειάζεται να γνωρίζουμε τίποτα για την εσωτερική υλοποίηση.

Φανταστείτε τώρα ότι η υπογραφή μιας συνάρτησης μοιάζει ως εξής:

function addition(float $x): float

Μια πρόσθεση με μία παράμετρο; Αυτό είναι παράξενο… Τι λέτε για αυτό;

function addition(): float

Αυτό είναι πραγματικά περίεργο, έτσι δεν είναι; Πώς νομίζεις ότι χρησιμοποιείται η λειτουργία;

echo addition(); // τι εκτυπώνει;

Κοιτάζοντας έναν τέτοιο κώδικα, είμαστε μπερδεμένοι. Όχι μόνο ένας αρχάριος δεν θα τον καταλάβαινε, ακόμη και ένας εξειδικευμένος προγραμματιστής δεν θα καταλάβαινε έναν τέτοιο κώδικα.

Αναρωτιέστε πώς θα έμοιαζε στην πραγματικότητα μια τέτοια συνάρτηση στο εσωτερικό της; Πού θα έβρισκε τους αθροιστές; Πιθανότατα θα τους έβρισκε με κάποιο τρόπο από μόνη της, κάπως έτσι:

function addition(): float
{
	$a = Input::get('a');
	$b = Input::get('b');
	return $a + $b;
}

Αποδεικνύεται ότι υπάρχουν κρυφές συνδέσεις με άλλες συναρτήσεις (ή στατικές μεθόδους) στο σώμα της συνάρτησης, και για να μάθουμε από πού προέρχονται στην πραγματικότητα οι προσθετέοι, πρέπει να ψάξουμε περισσότερο.

Όχι με αυτόν τον τρόπο!

Το σχέδιο που μόλις μας παρουσιάστηκε είναι η ουσία πολλών αρνητικών χαρακτηριστικών:

  • Η υπογραφή της συνάρτησης προσποιούνταν ότι δεν χρειαζόταν προσθετέα, γεγονός που μας μπέρδευε
  • δεν έχουμε ιδέα πώς να κάνουμε τη συνάρτηση να υπολογίζει με δύο άλλους αριθμούς
  • έπρεπε να κοιτάξουμε τον κώδικα για να δούμε πού παίρνει τους προσθετέους
  • ανακαλύψαμε κρυφές δεσμεύσεις
  • για να κατανοήσουμε πλήρως, πρέπει να εξερευνήσουμε και αυτές τις δεσμεύσεις

Και μήπως είναι καν δουλειά της συνάρτησης πρόσθεσης να προμηθεύεται εισόδους; Φυσικά και δεν είναι. Η ευθύνη της είναι μόνο να προσθέτει.

Δεν θέλουμε να συναντήσουμε τέτοιο κώδικα και σίγουρα δεν θέλουμε να τον γράψουμε. Η λύση είναι απλή: επιστρέψτε στα βασικά και χρησιμοποιήστε απλά παραμέτρους:

function addition(float $a, float $b): float
{
	return $a + $b;
}

Κανόνας #1: Αφήστε να περάσει σε σας

Ο πιο σημαντικός κανόνας είναι: Όλα τα δεδομένα που χρειάζονται οι συναρτήσεις ή οι κλάσεις πρέπει να τους μεταβιβάζονται.

Αντί να εφευρίσκετε κρυφούς μηχανισμούς για να τις βοηθήσετε να τα φτάσουν με κάποιο τρόπο μόνες τους, απλά περάστε τις παραμέτρους. Θα γλιτώσετε το χρόνο που χρειάζεται για να επινοήσετε κρυφό τρόπο, ο οποίος σίγουρα δεν θα βελτιώσει τον κώδικά σας.

Αν ακολουθείτε αυτόν τον κανόνα πάντα και παντού, βρίσκεστε στο δρόμο για κώδικα χωρίς κρυφές δεσμεύσεις. Προς κώδικα που είναι κατανοητός όχι μόνο για τον συγγραφέα, αλλά και για οποιονδήποτε τον διαβάσει στη συνέχεια. Όπου τα πάντα είναι κατανοητά από τις υπογραφές των συναρτήσεων και των κλάσεων και δεν χρειάζεται να ψάχνετε για κρυμμένα μυστικά στην υλοποίηση.

Αυτή η τεχνική ονομάζεται επιδέξια έγχυση εξάρτησης. Και τα δεδομένα ονομάζονται εξαρτήσεις. Αλλά πρόκειται για μια απλή μεταβίβαση παραμέτρων, τίποτα περισσότερο.

Παρακαλώ μην συγχέετε την έγχυση εξαρτήσεων, που είναι ένα πρότυπο σχεδίασης, με το “dependency injection container”, που είναι ένα εργαλείο, κάτι εντελώς διαφορετικό. Θα συζητήσουμε τα containers αργότερα.

Από τις συναρτήσεις στις κλάσεις

Και πώς σχετίζονται οι κλάσεις με αυτό; Μια κλάση είναι μια πιο σύνθετη οντότητα από μια απλή συνάρτηση, αλλά ο κανόνας #1 ισχύει και εδώ. Απλά υπάρχουν περισσότεροι τρόποι για να περάσετε ορίσματα. Για παράδειγμα, αρκετά παρόμοια με την περίπτωση μιας συνάρτησης:

class Math
{
	public function addition(float $a, float $b): float
	{
		return $a + $b;
	}
}

$math = new Math;
echo $math->addition(23, 1); // 24

Ή με τη χρήση άλλων μεθόδων, ή απευθείας από τον κατασκευαστή:

class Addition
{
	public function __construct(
		private float $a,
		private float $b,
	) {
	}

	public function calculate(): float
	{
		return $this->a + $this->b;
	}

}

$addition = new Addition(23, 1);
echo $addition->calculate(); // 24

Και τα δύο παραδείγματα συμμορφώνονται πλήρως με την έγχυση εξάρτησης.

Παραδείγματα πραγματικής ζωής

Στον πραγματικό κόσμο, δεν θα γράψετε κλάσεις για την πρόσθεση αριθμών. Ας προχωρήσουμε σε παραδείγματα του πραγματικού κόσμου.

Ας έχουμε μια κλάση Article που αναπαριστά ένα άρθρο ιστολογίου:

class Article
{
	public int $id;
	public string $title;
	public string $content;

	public function save(): void
	{
		// αποθήκευση του άρθρου στη βάση δεδομένων
	}
}

και η χρήση θα είναι η εξής:

$article = new Article;
$article->title = '10 Things You Need to Know About Losing Weight';
$article->content = 'Every year millions of people in ...';
$article->save();

Η μέθοδος save() αποθηκεύει το άρθρο σε έναν πίνακα της βάσης δεδομένων. Η υλοποίησή της με τη χρήση της Nette Database θα ήταν πανεύκολη, αν δεν υπήρχε ένα πρόβλημα: από πού θα πρέπει να πάρει το Article τη σύνδεση με τη βάση δεδομένων, δηλαδή το αντικείμενο της κλάσης Nette\Database\Connection;

Φαίνεται ότι έχουμε πολλές επιλογές. Μπορεί να την πάρει από κάπου σε μια στατική μεταβλητή. Ή να την κληρονομήσει από μια κλάση που θα παρέχει τη σύνδεση με τη βάση δεδομένων. Ή να εκμεταλλευτεί ένα singleton. Ή τα λεγόμενα facades που χρησιμοποιούνται στο Laravel:

use Illuminate\Support\Facades\DB;

class Article
{
	public int $id;
	public string $title;
	public string $content;

	public function save(): void
	{
		DB::insert(
			'INSERT INTO articles (title, content) VALUES (?, ?)',
			[$this->title, $this->content],
		);
	}
}

Υπέροχα, έχουμε λύσει το πρόβλημα.

Ή μήπως όχι;

Ας θυμηθούμε τον κανόνα #1: Αφήστε να περάσει σε σας You: όλες οι εξαρτήσεις που χρειάζεται η κλάση πρέπει να περάσουν σε αυτήν. Γιατί αν δεν το κάνουμε και παραβιάσουμε τον κανόνα, έχουμε ξεκινήσει το δρόμο για βρώμικο κώδικα γεμάτο κρυφές δεσμεύσεις, ακατανόητο και το αποτέλεσμα θα είναι μια εφαρμογή που θα είναι μπελάς στη συντήρηση και την ανάπτυξη.

Ο χρήστης της κλάσης Article δεν έχει ιδέα πού αποθηκεύει το άρθρο η μέθοδος save(). Σε έναν πίνακα της βάσης δεδομένων; Σε ποιον, στην παραγωγή ή στην ανάπτυξη; Και πώς μπορεί να αλλάξει αυτό;

Ο χρήστης πρέπει να εξετάσει τον τρόπο υλοποίησης της μεθόδου save() για να βρει τη χρήση της μεθόδου DB::insert(). Έτσι, πρέπει να ψάξει περαιτέρω για να βρει πώς αυτή η μέθοδος προμηθεύεται μια σύνδεση με τη βάση δεδομένων. Και οι κρυφές συνδέσεις μπορούν να σχηματίσουν μια αρκετά μεγάλη αλυσίδα.

Οι κρυφές δεσμεύσεις, οι προσόψεις του Laravel ή οι στατικές μεταβλητές δεν υπάρχουν ποτέ σε καθαρό, καλά σχεδιασμένο κώδικα. Στον καθαρό και καλά σχεδιασμένο κώδικα, τα ορίσματα μεταβιβάζονται:

class Article
{
	public function save(Nette\Database\Connection $db): void
	{
		$db->query('INSERT INTO articles', [
			'title' => $this->title,
			'content' => $this->content,
		]);
	}
}

Ακόμα πιο πρακτικό, όπως θα δούμε στη συνέχεια, είναι η χρήση ενός κατασκευαστή:

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function save(): void
	{
		$this->db->query('INSERT INTO articles', [
			'title' => $this->title,
			'content' => $this->content,
		]);
	}
}

Αν είστε έμπειρος προγραμματιστής, ίσως σκέφτεστε ότι το Article δεν θα έπρεπε να έχει καθόλου μέθοδο save(), θα έπρεπε να είναι ένα καθαρό στοιχείο δεδομένων και ένα ξεχωριστό αποθετήριο θα έπρεπε να φροντίζει για την αποθήκευση. Αυτό είναι λογικό. Αλλά αυτό θα μας πήγαινε πολύ πέρα από το θέμα, που είναι η έγχυση εξαρτήσεων, και θα προσπαθούσαμε να δώσουμε απλά παραδείγματα.

Αν πρόκειται να γράψετε μια κλάση που απαιτεί μια βάση δεδομένων για να λειτουργήσει, για παράδειγμα, μην βρείτε από πού θα την πάρετε, αλλά να σας την περάσετε. Ίσως ως παράμετρος σε έναν κατασκευαστή ή σε μια άλλη μέθοδο. Δηλώστε εξαρτήσεις. Εκθέστε τις στο API της κλάσης σας. Θα έχετε κατανοητό και προβλέψιμο κώδικα.

Τι θα λέγατε για αυτή την κλάση που καταγράφει μηνύματα σφάλματος:

class Logger
{
	public function log(string $message)
	{
		$file = LOG_DIR . '/log.txt';
		file_put_contents($file, $message . "\n", FILE_APPEND);
	}
}

Τι νομίζετε, ακολουθήσαμε τον κανόνα #1: Αφήστε να περάσει σε σας;

Δεν το κάναμε.

Η βασική πληροφορία, ο κατάλογος του αρχείου καταγραφής, αποκτάται από την κλάση από τη σταθερά.

Δείτε το παράδειγμα χρήσης:

$logger = new Logger;
$logger->log('The temperature is 23 °C');
$logger->log('The temperature is 10 °C');

Χωρίς να γνωρίζετε την εφαρμογή, μπορείτε να απαντήσετε στην ερώτηση πού γράφονται τα μηνύματα; Θα σας έδειχνε ότι η ύπαρξη της σταθεράς LOG_DIR είναι απαραίτητη για να λειτουργήσει; Και θα μπορούσατε να δημιουργήσετε μια δεύτερη περίπτωση που να γράφει σε διαφορετική θέση; Σίγουρα όχι.

Ας διορθώσουμε την κλάση:

class Logger
{
	public function __construct(
		private string $file,
	) {
	}

	public function log(string $message): void
	{
		file_put_contents($this->file, $message . "\n", FILE_APPEND);
	}
}

Η κλάση είναι τώρα πολύ πιο σαφής, πιο παραμετροποιήσιμη και επομένως πιο χρήσιμη.

$logger = new Logger('/path/to/log.txt');
$logger->log('The temperature is 15 °C');

Αλλά δεν με νοιάζει!

“Όταν δημιουργώ ένα αντικείμενο Article και καλώ την save(), δεν θέλω να ασχοληθώ με τη βάση δεδομένων, θέλω απλώς να αποθηκευτεί σε αυτήν που έχω ορίσει στη διαμόρφωση. ”

“Όταν χρησιμοποιώ το Logger, θέλω απλώς να γράφεται το μήνυμα και δεν θέλω να ασχοληθώ με το πού. Αφήστε να χρησιμοποιηθούν οι παγκόσμιες ρυθμίσεις. ”

Αυτά είναι σωστά σχόλια.

Ως παράδειγμα, ας πάρουμε μια τάξη που στέλνει ενημερωτικά δελτία και καταγράφει πώς πήγε αυτό:

class NewsletterDistributor
{
	public function distribute(): void
	{
		$logger = new Logger(/* ... */);
		try {
			$this->sendEmails();
			$logger->log('Emails have been sent out');

		} catch (Exception $e) {
			$logger->log('An error occurred during the sending');
			throw $e;
		}
	}
}

Το βελτιωμένο Logger, το οποίο δεν χρησιμοποιεί πλέον τη σταθερά LOG_DIR, απαιτεί μια διαδρομή αρχείου στον κατασκευαστή. Πώς να το λύσετε αυτό; Η κλάση NewsletterDistributor δεν ενδιαφέρεται για το πού γράφονται τα μηνύματα, απλά θέλει να τα γράψει.

Η λύση είναι και πάλι ο κανόνας #1: Αφήστε να περάσει σε σας: περάστε όλα τα δεδομένα που χρειάζεται η κλάση σε αυτήν.

Έτσι, περνάμε τη διαδρομή προς το αρχείο καταγραφής στον κατασκευαστή, τον οποίο στη συνέχεια χρησιμοποιούμε για να δημιουργήσουμε το αντικείμενο Logger?

class NewsletterDistributor
{
	public function __construct(
		private string $file, // ⛔ ΌΧΙ ΜΕ ΑΥΤΌΝ ΤΟΝ ΤΡΌΠΟ!
	) {
	}

	public function distribute(): void
	{
		$logger = new Logger($this->file);

Όχι έτσι! Επειδή η διαδρομή δεν ανήκει στα δεδομένα που χρειάζεται η κλάση NewsletterDistributor – χρειάζεται το Logger. Η κλάση χρειάζεται τον ίδιο τον καταγραφέα. Και αυτό είναι που θα μεταβιβάσουμε:

class NewsletterDistributor
{
	public function __construct(
		private Logger $logger, // ✅
	) {
	}

	public function distribute(): void
	{
		try {
			$this->sendEmails();
			$this->logger->log('Emails have been sent out');

		} catch (Exception $e) {
			$this->logger->log('An error occurred during the sending');
			throw $e;
		}
	}
}

Τώρα είναι σαφές από τις υπογραφές της κλάσης NewsletterDistributor ότι η καταγραφή αποτελεί μέρος της λειτουργικότητάς της. Και το έργο της αντικατάστασης του καταγραφέα με έναν άλλο, ίσως για σκοπούς δοκιμής, είναι αρκετά ασήμαντο. Επιπλέον, αν αλλάξει ο κατασκευαστής της κλάσης Logger, αυτό δεν θα έχει καμία επίδραση στην κλάση μας.

Κανόνας #2: Πάρτε αυτό που σας ανήκει

Μην παραπλανηθείτε και μην αφήνετε τις παραμέτρους των εξαρτήσεών σας να σας μεταβιβάζονται. Περάστε τις εξαρτήσεις απευθείας.

Αυτό θα κάνει τον κώδικα που χρησιμοποιεί άλλα αντικείμενα εντελώς ανεξάρτητο από τις αλλαγές στους κατασκευαστές τους. Το API του θα είναι πιο αληθινό. Και το πιο σημαντικό, θα είναι τετριμμένο να ανταλλάξετε αυτές τις εξαρτήσεις με άλλες.

Ένα νέο μέλος της οικογένειας

Η ομάδα ανάπτυξης αποφάσισε να δημιουργήσει έναν δεύτερο καταγραφέα που γράφει στη βάση δεδομένων. Έτσι δημιουργούμε μια κλάση DatabaseLogger. Έτσι έχουμε δύο κλάσεις, Logger και DatabaseLogger, η μία γράφει σε ένα αρχείο, η άλλη γράφει σε μια βάση δεδομένων … δεν νομίζετε ότι υπάρχει κάτι περίεργο σε αυτό το όνομα; Δεν θα ήταν καλύτερα να μετονομάσουμε την Logger σε FileLogger; Σίγουρα θα ήταν καλύτερα.

Αλλά ας το κάνουμε έξυπνα. Θα δημιουργήσουμε μια διεπαφή με το αρχικό όνομα:

interface Logger
{
	function log(string $message): void;
}

…την οποία θα υλοποιούν και οι δύο καταγραφείς:

class FileLogger implements Logger
// ...

class DatabaseLogger implements Logger
// ...

Και με αυτόν τον τρόπο, τίποτα δεν θα χρειαστεί να αλλάξει στον υπόλοιπο κώδικα όπου χρησιμοποιείται ο καταγραφέας. Για παράδειγμα, ο κατασκευαστής της κλάσης NewsletterDistributor θα εξακολουθεί να είναι ευχαριστημένος με την απαίτηση του Logger ως παραμέτρου. Και θα εξαρτάται από εμάς ποια περίπτωση θα του δώσουμε.

Αυτός είναι ο λόγος για τον οποίο δεν δίνουμε ποτέ στα ονόματα διεπαφών την κατάληξη Interface ή το πρόθεμα I. Διαφορετικά, θα ήταν αδύνατο να αναπτύξουμε κώδικα τόσο όμορφα.

Χιούστον, έχουμε ένα πρόβλημα

Ενώ στο σύνολο της εφαρμογής μπορούμε να είμαστε ευχαριστημένοι με μια μόνο περίπτωση ενός καταγραφέα, είτε πρόκειται για αρχείο είτε για βάση δεδομένων, και απλά να την περνάμε όπου καταγράφεται κάτι, τα πράγματα είναι εντελώς διαφορετικά στην περίπτωση της κλάσης Article. Για την ακρίβεια, δημιουργούμε περιπτώσεις της ανάλογα με τις ανάγκες μας, ενδεχομένως πολλές φορές. Πώς θα αντιμετωπίσουμε τη δέσμευση της βάσης δεδομένων στον κατασκευαστή της;

Ως παράδειγμα, μπορούμε να χρησιμοποιήσουμε έναν ελεγκτή που θα πρέπει να αποθηκεύσει ένα άρθρο στη βάση δεδομένων μετά την υποβολή μιας φόρμας:

class EditController extends Controller
{
	public function formSubmitted($data)
	{
		$article = new Article(/* ... */);
		$article->title = $data->title;
		$article->content = $data->content;
		$article->save();
	}
}

Μια πιθανή λύση προσφέρεται άμεσα: να περάσει το αντικείμενο της βάσης δεδομένων από τον κατασκευαστή στο EditController και να χρησιμοποιηθεί το $article = new Article($this->db).

Όπως και στην προηγούμενη περίπτωση με το Logger και τη διαδρομή του αρχείου, αυτή δεν είναι η σωστή προσέγγιση. Η βάση δεδομένων δεν αποτελεί εξάρτηση του EditController, αλλά του Article. Έτσι, η μεταβίβαση της βάσης δεδομένων αντιβαίνει στον κανόνα #2: πάρτε ό,τι σας ανήκει. Όταν ο κατασκευαστής της κλάσης Article τροποποιείται (προστίθεται μια νέα παράμετρος), θα πρέπει να τροποποιηθεί και ο κώδικας σε όλα τα σημεία όπου δημιουργούνται περιπτώσεις. Ufff.

Χιούστον, τι προτείνεις;

Κανόνας #3: Αφήστε το εργοστάσιο να το χειριστεί

Αφαιρώντας τις κρυφές δεσμεύσεις και περνώντας όλες τις εξαρτήσεις ως ορίσματα, παίρνουμε πιο παραμετροποιήσιμες και ευέλικτες κλάσεις. Και έτσι χρειαζόμαστε κάτι άλλο για να δημιουργήσουμε και να διαμορφώσουμε αυτές τις πιο ευέλικτες κλάσεις. Θα το ονομάσουμε εργοστάσια.

Ο γενικός κανόνας είναι: αν μια κλάση έχει εξαρτήσεις, αφήστε τη δημιουργία των περιπτώσεων τους στο εργοστάσιο.

Τα εργοστάσια είναι μια πιο έξυπνη αντικατάσταση του τελεστή new στον κόσμο του dependency injection.

Μην συγχέετε με το πρότυπο σχεδίασης factory method, το οποίο περιγράφει έναν συγκεκριμένο τρόπο χρήσης των εργοστασίων και δεν σχετίζεται με αυτό το θέμα.

Εργοστάσιο

Ένα εργοστάσιο είναι μια μέθοδος ή μια κλάση που παράγει και διαμορφώνει αντικείμενα. Ονομάζουμε Article που παράγει την κλάση ArticleFactory και θα μπορούσε να μοιάζει ως εξής:

class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}

Η χρήση του στον ελεγκτή θα ήταν η εξής:

class EditController extends Controller
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function formSubmitted($data)
	{
		// αφήστε το εργοστάσιο να δημιουργήσει ένα αντικείμενο
		$article = $this->articleFactory->create();
		$article->title = $data->title;
		$article->content = $data->content;
		$article->save();
	}
}

Σε αυτό το σημείο, όταν η υπογραφή του κατασκευαστή της κλάσης Article αλλάζει, το μόνο μέρος του κώδικα που χρειάζεται να ανταποκριθεί είναι το ίδιο το εργοστάσιο ArticleFactory. Οποιοσδήποτε άλλος κώδικας που εργάζεται με αντικείμενα Article, όπως το EditController, δεν θα επηρεαστεί.

Μπορεί να χτυπάτε το μέτωπό σας αυτή τη στιγμή και να αναρωτιέστε αν βοηθηθήκαμε καθόλου. Η ποσότητα του κώδικα έχει αυξηθεί και το όλο θέμα αρχίζει να φαίνεται ύποπτα περίπλοκο.

Μην ανησυχείτε, σύντομα θα φτάσουμε στο δοχείο Nette DI. Και έχει αρκετούς άσσους στο μανίκι του που θα κάνουν τη δημιουργία εφαρμογών με χρήση dependency injection εξαιρετικά απλή. Για παράδειγμα, αντί για την κλάση ArticleFactory, θα αρκεί να γράψετε μια απλή διεπαφή:

interface ArticleFactory
{
	function create(): Article;
}

Αλλά προχωράμε μπροστά, περιμένετε :-)

Περίληψη

Στην αρχή αυτού του κεφαλαίου, υποσχεθήκαμε να σας δείξουμε έναν τρόπο σχεδίασης καθαρού κώδικα. Απλά δώστε στις κλάσεις

Μπορεί να μη φαίνεται έτσι με την πρώτη ματιά, αλλά αυτοί οι τρεις κανόνες έχουν εκτεταμένες επιπτώσεις. Οδηγούν σε μια ριζικά διαφορετική θεώρηση του σχεδιασμού κώδικα. Αξίζει τον κόπο; Οι προγραμματιστές που έχουν απορρίψει τις παλιές συνήθειες και έχουν αρχίσει να χρησιμοποιούν με συνέπεια την έγχυση εξαρτήσεων θεωρούν ότι πρόκειται για μια κομβική στιγμή στην επαγγελματική τους ζωή. Άνοιξε έναν κόσμο ξεκάθαρων και βιώσιμων εφαρμογών.

Τι γίνεται όμως αν ο κώδικας δεν χρησιμοποιεί με συνέπεια την έγχυση εξαρτήσεων; Τι γίνεται αν είναι βασισμένος σε στατικές μεθόδους ή singletons; Μήπως αυτό επιφέρει προβλήματα; Ναι, και είναι πολύ σημαντικά.

έκδοση: 3.x