Uvod v objektno usmerjeno programiranje
Izraz “OOP” pomeni objektno usmerjeno programiranje, ki je način organiziranja in strukturiranja kode. OOP nam omogoča, da na program gledamo kot na zbirko predmetov, ki med seboj komunicirajo, in ne kot na zaporedje ukazov in funkcij.
V OOP je “objekt” enota, ki vsebuje podatke in funkcije, ki delujejo na teh podatkih. Objekti so ustvarjeni na podlagi “razredov”, ki jih lahko razumemo kot načrte ali predloge za objekte. Ko imamo razred, lahko ustvarimo njegov “primerek”, ki je poseben objekt, narejen iz tega razreda.
Oglejmo si, kako lahko ustvarimo preprost razred v PHP. Pri definiranju razreda uporabimo ključno besedo “class”, ki ji sledi ime razreda, nato pa še oglati oklepaji, ki zapirajo funkcije razreda (imenovane “metode”) in spremenljivke razreda (imenovane “lastnosti” ali “atributi”):
class Car
{
function honk()
{
echo 'Beep beep!';
}
}
V tem primeru smo ustvarili razred z imenom Car
z eno funkcijo (ali “metodo”) z imenom
honk
.
Vsak razred mora rešiti le eno glavno nalogo. Če razred opravlja preveč stvari, ga je morda primerno razdeliti na manjše, specializirane razrede.
Razredi so običajno shranjeni v ločenih datotekah, da bi bila koda urejena in bi bilo po njej lažje krmariti. Ime datoteke
se mora ujemati z imenom razreda, tako da bi bilo ime datoteke za razred Car
Car.php
.
Pri poimenovanju razredov je dobro upoštevati konvencijo “PascalCase”, kar pomeni, da se vsaka beseda v imenu začne z veliko črko in ni podčrtank ali drugih ločil. Za metode in lastnosti velja konvencija “camelCase”, kar pomeni, da se začnejo z malo črko.
Nekatere metode v PHP imajo posebne vloge in imajo predpono __
(dva podčrtaja). Ena najpomembnejših posebnih
metod je “konstruktor”, ki je označen kot __construct
. Konstruktor je metoda, ki se samodejno pokliče pri
ustvarjanju novega primerka razreda.
Konstruktor pogosto uporabljamo za določanje začetnega stanja predmeta. Na primer, ko ustvarjate objekt, ki predstavlja osebo, lahko konstruktor uporabite za nastavitev njene starosti, imena ali drugih atributov.
Oglejmo si, kako uporabiti konstruktor v PHP:
class Person
{
private $age;
function __construct($age)
{
$this->age = $age;
}
function howOldAreYou()
{
return $this->age;
}
}
$person = new Person(25);
echo $person->howOldAreYou(); // Outputs: 25
V tem primeru ima razred Person
lastnost $age
in konstruktor, ki to lastnost nastavi. Metoda
howOldAreYou()
nato omogoča dostop do starosti osebe.
Ključna beseda new
se uporablja za ustvarjanje novega primerka razreda. V zgornjem primeru smo ustvarili novo
osebo, staro 25 let.
Nastavite lahko tudi privzete vrednosti za parametre konstruktorja, če niso določeni pri ustvarjanju predmeta. Na primer:
class Person
{
private $age;
function __construct($age = 20)
{
$this->age = $age;
}
function howOldAreYou()
{
return $this->age;
}
}
$person = new Person; // if no argument is passed, parentheses can be omitted
echo $person->howOldAreYou(); // Outputs: 20
Če pri ustvarjanju predmeta Person
ne določite starosti, bo uporabljena privzeta vrednost 20.
Nazadnje lahko opredelitev lastnosti z njeno inicializacijo prek konstruktorja skrajšate in poenostavite na naslednji način:
class Person
{
function __construct(
private $age = 20,
) {
}
}
Prostori imen
Prostori imen nam omogočajo organiziranje in združevanje sorodnih razredov, funkcij in konstant, pri čemer se izognemo konfliktom pri poimenovanju. Predstavljamo si jih lahko kot mape v računalniku, kjer vsaka mapa vsebuje datoteke, povezane z določenim projektom ali temo.
Prostori imen so še posebej uporabni v večjih projektih ali pri uporabi knjižnic tretjih oseb, kjer lahko pride do sporov v poimenovanju razredov.
Predstavljajte si, da imate v svojem projektu razred z imenom Car
in ga želite postaviti v imenski prostor
z imenom Transport
. To bi storili takole:
namespace Transport;
class Car
{
function honk()
{
echo 'Beep beep!';
}
}
Če želite razred Car
uporabiti v drugi datoteki, morate navesti, iz katerega imenskega prostora razred
izhaja:
$car = new Transport\Car;
Za poenostavitev lahko na začetku datoteke navedete, kateri razred iz določenega imenskega prostora želite uporabiti, kar vam omogoča ustvarjanje primerkov brez navajanja celotne poti:
use Transport\Car;
$car = new Car;
Dedovanje
Dedovanje je orodje objektno usmerjenega programiranja, ki omogoča ustvarjanje novih razredov na podlagi obstoječih, dedovanje njihovih lastnosti in metod ter njihovo razširitev ali redefiniranje po potrebi. Dedovanje zagotavlja ponovno uporabnost kode in hierarhijo razredov.
Preprosto povedano, če imamo en razred in želimo ustvariti drugega, ki izhaja iz njega, vendar z nekaterimi spremembami, lahko novi razred “podedujemo” od prvotnega.
V jeziku PHP se dedovanje izvaja s ključno besedo extends
.
Naš razred Person
shranjuje informacije o starosti. Imamo lahko še en razred, Student
, ki
razširja Person
in dodaja informacije o področju študija.
Oglejmo si primer:
class Person
{
private $age;
function __construct($age)
{
$this->age = $age;
}
function howOldAreYou()
{
return $this->age;
}
}
class Student extends Person
{
private $fieldOfStudy;
function __construct($age, $fieldOfStudy)
{
parent::__construct($age);
$this->fieldOfStudy = $fieldOfStudy;
}
function printInformation()
{
echo 'Age of student: ', $this->howOldAreYou();
echo 'Field of study: ', $this->fieldOfStudy;
}
}
$student = new Student(20, 'Computer Science');
$student->printInformation();
Kako deluje ta koda?
- S ključno besedo
extends
smo razširili razredPerson
, kar pomeni, da razredStudent
podeduje vse metode in lastnosti od razredaPerson
. - Ključna beseda
parent::
nam omogoča, da kličemo metode iz nadrejenega razreda. V tem primeru smo poklicali konstruktor iz razredaPerson
, preden smo v razredStudent
dodali svojo funkcionalnost.
Dedovanje je namenjeno situacijam, v katerih med razredi obstaja razmerje “is a”. Na primer, razred Student
je razred Person
. Mačka je žival. Omogoča nam, da v primerih, ko v kodi pričakujemo en objekt (npr.
“Oseba”), namesto njega uporabimo izpeljani objekt (npr. “Študent”).
Bistveno se je zavedati, da glavni namen dedovanja ni preprečevanje podvajanja kode. Ravno nasprotno, napačna uporaba dedovanja lahko privede do zapletene in težko vzdrževane kode. Če med razredi ni razmerja “je a”, moramo namesto dedovanja razmisliti o kompoziciji.
Sestavljanje
Kompozicija je tehnika, pri kateri namesto dedovanja lastnosti in metod iz drugega razreda preprosto uporabimo njegov primerek v svojem razredu. Tako lahko združimo funkcionalnosti in lastnosti več razredov, ne da bi ustvarjali zapletene strukture dedovanja.
Na primer, imamo razred Engine
in razred Car
. Namesto da bi rekli “Avto je motor”, rečemo
“Avto ima motor”, kar je tipično kompozicijsko razmerje.
class Engine
{
function start()
{
echo "Engine is running.";
}
}
class Car
{
private $engine;
function __construct()
{
$this->engine = new Engine;
}
function start()
{
$this->engine->start();
echo "The car is ready to drive!";
}
}
$car = new Car;
$car->start();
V tem primeru razred Car
nima vseh lastnosti in metod razreda Engine
, vendar ima do njih dostop prek
lastnosti razreda $engine
.
Prednost sestave je večja prilagodljivost načrtovanja in boljša prilagodljivost za prihodnje spremembe.
Vidnost
V jeziku PHP lahko za lastnosti, metode in konstante razreda določite vidnost. Vidnost določa, kje lahko dostopate do teh elementov.
- Public: Če je element označen kot
public
, to pomeni, da lahko do njega dostopate od koder koli, tudi zunaj razreda. - Zaščiten: Element, označen kot
protected
, je dostopen le znotraj razreda in vseh njegovih potomcev (razredov, ki dedujejo od njega). - Zasebno: Če je element označen kot
private
, lahko do njega dostopate le znotraj razreda, v katerem je bil opredeljen.
Če ne določite vidnosti, PHP samodejno nastavi vidnost na public
.
Oglejmo si vzorec kode:
class VisibilityExample
{
public $publicProperty = 'Public';
protected $protectedProperty = 'Protected';
private $privateProperty = 'Private';
public function printProperties()
{
echo $this->publicProperty; // Works
echo $this->protectedProperty; // Works
echo $this->privateProperty; // Works
}
}
$object = new VisibilityExample;
$object->printProperties();
echo $object->publicProperty; // Works
// echo $object->protectedProperty; // Throws an error
// echo $object->privateProperty; // Throws an error
Nadaljujemo z dedovanjem razredov:
class ChildClass extends VisibilityExample
{
public function printProperties()
{
echo $this->publicProperty; // Works
echo $this->protectedProperty; // Works
// echo $this->privateProperty; // Throws an error
}
}
V tem primeru lahko metoda printProperties()
v razredu ChildClass
dostopa do javnih in zaščitenih
lastnosti, ne more pa dostopati do zasebnih lastnosti nadrejenega razreda.
Podatki in metode morajo biti čim bolj skriti in dostopni le prek določenega vmesnika. Tako lahko spremenite notranjo implementacijo razreda, ne da bi to vplivalo na preostalo kodo.
Končna ključna beseda
V jeziku PHP lahko ključno besedo final
uporabimo, če želimo preprečiti, da bi razred, metodo ali konstanto
podedovali ali prepisali. Če je razred označen kot final
, ga ni mogoče razširiti. Če je metoda označena kot
final
, je ni mogoče prepisati v podrazredu.
Če se zavedamo, da določenega razreda ali metode ne bomo več spreminjali, lahko lažje izvajamo spremembe, ne da bi nas skrbelo za morebitne konflikte. Tako lahko na primer dodamo novo metodo, ne da bi se bali, da ima potomec že metodo z istim imenom, kar bi povzročilo kolizijo. Ali pa lahko spremenimo parametre metode, spet brez tveganja, da bi povzročili neskladje s prepisano metodo v potomcu.
final class FinalClass
{
}
// The following code will throw an error because we cannot inherit from a final class.
class ChildOfFinalClass extends FinalClass
{
}
V tem primeru bo poskus dedovanja iz končnega razreda FinalClass
povzročil napako.
Statične lastnosti in metode
Ko v jeziku PHP govorimo o “statičnih” elementih razreda, imamo v mislih metode in lastnosti, ki pripadajo samemu razredu in ne določenemu primerku razreda. To pomeni, da vam za dostop do njih ni treba ustvariti primerka razreda. Namesto tega jih pokličete ali do njih dostopate neposredno prek imena razreda.
Upoštevajte, da ker statični elementi pripadajo razredu in ne njegovim instancam, znotraj statičnih metod ne morete
uporabljati psevdo-premenljivke $this
.
Uporaba statičnih lastnosti vodi v obskurno kodo, polno pasti, zato jih nikoli ne smete uporabljati in tu ne bomo prikazali primera. Po drugi strani pa so statične metode uporabne. Tukaj je primer:
class Calculator
{
public static function add($a, $b)
{
return $a + $b;
}
public static function subtract($a, $b)
{
return $a - $b;
}
}
// Using the static method without creating an instance of the class
echo Calculator::add(5, 3); // Output: 8
echo Calculator::subtract(5, 3); // Output: 2
V tem primeru smo ustvarili razred Calculator
z dvema statičnima metodama. Ti metodi lahko pokličemo
neposredno, ne da bi ustvarili primerek razreda z uporabo operatorja ::
. Statične metode so še posebej uporabne za
operacije, ki niso odvisne od stanja določenega primerka razreda.
Konstante razreda
Znotraj razredov lahko določimo konstante. Konstante so vrednosti, ki se med izvajanjem programa nikoli ne spremenijo. V nasprotju s spremenljivkami ostane vrednost konstante enaka.
class Car
{
public const NumberOfWheels = 4;
public function displayNumberOfWheels(): int
{
echo self::NumberOfWheels;
}
}
echo Car::NumberOfWheels; // Output: 4
V tem primeru imamo razred Car
s konstanto NumberOfWheels
. Pri dostopu do konstante znotraj razreda
lahko namesto imena razreda uporabimo ključno besedo self
.
Objektni vmesniki
Objektni vmesniki delujejo kot “pogodbe” za razrede. Če naj razred implementira objektni vmesnik, mora vsebovati vse metode, ki jih vmesnik opredeljuje. To je odličen način za zagotavljanje, da se določeni razredi držijo iste “pogodbe” ali strukture.
V PHP so vmesniki opredeljeni s ključno besedo interface
. Vse metode, opredeljene v vmesniku, so javne
(public
). Ko razred implementira vmesnik, uporabi ključno besedo implements
.
interface Animal
{
function makeSound();
}
class Cat implements Animal
{
public function makeSound()
{
echo 'Meow';
}
}
$cat = new Cat;
$cat->makeSound();
Če razred implementira vmesnik, vendar niso definirane vse pričakovane metode, PHP vrže napako. Razred lahko implementira več vmesnikov hkrati, kar se razlikuje od dedovanja, pri katerem lahko razred deduje samo od enega razreda.
Abstraktni razredi
Abstraktni razredi služijo kot osnovne predloge za druge razrede, vendar njihovih primerkov ne morete ustvariti neposredno. Vsebujejo mešanico popolnih metod in abstraktnih metod, ki nimajo določene vsebine. Razredi, ki dedujejo od abstraktnih razredov, morajo zagotoviti definicije za vse abstraktne metode iz starševskega razreda.
Za opredelitev abstraktnega razreda uporabimo ključno besedo abstract
.
abstract class AbstractClass
{
public function regularMethod()
{
echo 'This is a regular method';
}
abstract public function abstractMethod();
}
class Child extends AbstractClass
{
public function abstractMethod()
{
echo 'This is the implementation of the abstract method';
}
}
$instance = new Child;
$instance->regularMethod();
$instance->abstractMethod();
V tem primeru imamo abstraktni razred z eno redno in eno abstraktno metodo. Nato imamo razred Child
, ki podeduje
od AbstractClass
in zagotavlja implementacijo abstraktne metode.
Preverjanje tipa
Pri programiranju je ključnega pomena, da zagotovimo, da so podatki, s katerimi delamo, pravilnega tipa. V jeziku PHP imamo na voljo orodja, ki to zagotavljajo. Preverjanje, ali so podatki pravilnega tipa, se imenuje “preverjanje tipa”.
Tipi, ki jih lahko srečamo v PHP:
- Osnovne vrste: To so
int
(cela števila),float
(števila s plavajočo vejico),bool
(logične vrednosti),string
(nizi),array
(polja) innull
. - Tredi: Kadar želimo, da je vrednost primerek določenega razreda.
- Vmesniki: Opredeljuje nabor metod, ki jih mora razred implementirati. Vrednost, ki ustreza vmesniku, mora imeti te metode.
- Mešani tipi: Določimo lahko, da ima lahko spremenljivka več dovoljenih tipov.
- Void: Ta posebni tip označuje, da funkcija ali metoda ne vrača nobene vrednosti.
Oglejmo si, kako spremeniti kodo, da bo vključevala tipe:
class Person
{
private int $age;
public function __construct(int $age)
{
$this->age = $age;
}
public function printAge(): void
{
echo "This person is " . $this->age . " years old.";
}
}
/**
* A function that accepts a Person object and prints the person's age.
*/
function printPersonAge(Person $person): void
{
$person->printAge();
}
Na ta način zagotovimo, da naša koda pričakuje in dela s podatki pravilne vrste, kar nam pomaga preprečiti morebitne napake.
Primerjava in identiteta
V PHP lahko predmete primerjate na dva načina:
- Primerjava vrednosti
==
: Preveri, ali sta objekta istega razreda in imata v svojih lastnostih enake vrednosti. - Primerjava identitete
===
: Preveri, ali gre za isti primerek objekta.
class Car
{
public string $brand;
public function __construct(string $brand)
{
$this->brand = $brand;
}
}
$car1 = new Car('Skoda');
$car2 = new Car('Skoda');
$car3 = $car1;
var_dump($car1 == $car2); // true, because they have the same value
var_dump($car1 === $car2); // false, because they are not the same instance
var_dump($car1 === $car3); // true, because $car3 is the same instance as $car1
Upravljavec spletne strani instanceof
Operator instanceof
omogoča ugotavljanje, ali je dani predmet primerek določenega razreda, potomec tega razreda
ali pa implementira določen vmesnik.
Predstavljajte si, da imamo razred Person
in še en razred Student
, ki je potomec razreda
Person
:
class Person
{
private int $age;
public function __construct(int $age)
{
$this->age = $age;
}
}
class Student extends Person
{
private string $major;
public function __construct(int $age, string $major)
{
parent::__construct($age);
$this->major = $major;
}
}
$student = new Student(20, 'Computer Science');
// Check if $student is an instance of the Student class
var_dump($student instanceof Student); // Output: bool(true)
// Check if $student is an instance of the Person class (because Student is a descendant of Person)
var_dump($student instanceof Person); // Output: bool(true)
Iz izpisov je razvidno, da objekt $student
velja za primerek obeh razredov Student
in
Person
.
Fluentni vmesniki
“Fluentni vmesnik” je tehnika v OOP, ki omogoča veriženje metod v enem samem klicu. To pogosto poenostavi in razjasni kodo.
Ključni element tekočega vmesnika je, da vsaka metoda v verigi vrne referenco na trenutni objekt. To dosežemo z uporabo
return $this;
na koncu metode. Ta slog programiranja je pogosto povezan z metodami, imenovanimi “nastavljalci”,
ki nastavljajo vrednosti lastnosti objekta.
Oglejmo si, kako bi lahko bil videti tekoči vmesnik za pošiljanje e-pošte:
public function sendMessage()
{
$email = new Email;
$email->setFrom('sender@example.com')
->setRecipient('admin@example.com')
->setMessage('Hello, this is a message.')
->send();
}
V tem primeru so metode setFrom()
, setRecipient()
in setMessage()
uporabljene za
nastavitev ustreznih vrednosti (pošiljatelj, prejemnik, vsebina sporočila). Po nastavitvi vsake od teh vrednosti metode vrnejo
trenutni objekt ($email
), kar nam omogoča, da za njo verižno priključimo drugo metodo. Na koncu pokličemo metodo
send()
, ki dejansko pošlje elektronsko sporočilo.
Zahvaljujoč tekočim vmesnikom lahko pišemo kodo, ki je intuitivna in lahko berljiva.
Kopiranje s clone
V jeziku PHP lahko ustvarimo kopijo predmeta z uporabo operatorja clone
. Tako dobimo nov primerek z enako
vsebino.
Če moramo pri kopiranju objekta spremeniti nekatere njegove lastnosti, lahko v razredu definiramo posebno metodo
__clone()
. Ta metoda se samodejno pokliče, ko je objekt kloniran.
class Sheep
{
public string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function __clone()
{
$this->name = 'Clone of ' . $this->name;
}
}
$original = new Sheep('Dolly');
echo $original->name . "\n"; // Outputs: Dolly
$clone = clone $original;
echo $clone->name . "\n"; // Outputs: Clone of Dolly
V tem primeru imamo razred Sheep
z eno lastnostjo $name
. Ko kloniramo primerek tega razreda, metoda
__clone()
poskrbi, da ime klonirane ovce dobi predpono “Clone of”.
Lastnosti
Značilnosti v PHP so orodje, ki omogoča souporabo metod, lastnosti in konstant med razredi ter preprečuje podvajanje kode. Lahko si jih predstavljate kot mehanizem “kopiraj in prilepi” (Ctrl-C in Ctrl-V), pri katerem se vsebina lastnosti “prilepi” v razrede. To vam omogoča ponovno uporabo kode, ne da bi morali ustvarjati zapletene hierarhije razredov.
Oglejmo si preprost primer uporabe lastnosti v jeziku PHP:
trait Honking
{
public function honk()
{
echo 'Beep beep!';
}
}
class Car
{
use Honking;
}
class Truck
{
use Honking;
}
$car = new Car;
$car->honk(); // Outputs 'Beep beep!'
$truck = new Truck;
$truck->honk(); // Also outputs 'Beep beep!'
V tem primeru imamo lastnost z imenom Honking
, ki vsebuje eno metodo honk()
. Nato imamo dva
razreda: Car
in Truck
, ki oba uporabljata lastnost Honking
. Posledično oba razreda
“imata” metodo honk()
in jo lahko kličemo na predmetih obeh razredov.
Značilnosti omogočajo enostavno in učinkovito izmenjavo kode med razredi. Ne vstopajo v hierarhijo dedovanja, tj.
$car instanceof Honking
bo vrnil false
.
Izjeme
Izjeme v OOP nam omogočajo obravnavo in upravljanje napak, ki se lahko pojavijo med izvajanjem naše kode. V bistvu so predmeti, namenjeni beleženju napak ali nepričakovanih situacij v vašem programu.
V PHP je za te predmete vgrajen razred Exception
. Ima več metod, ki nam omogočajo, da pridobimo več informacij
o izjemi, kot so sporočilo o napaki, datoteka in vrstica, v kateri se je napaka pojavila, itd.
Ko se pojavi težava, lahko “vržemo” izjemo (z uporabo razreda throw
). Če želimo to izjemo “ujeti” in
obdelati, uporabimo bloka try
in catch
.
Oglejmo si, kako to deluje:
try {
throw new Exception('Message explaining the reason for the exception');
// This code won't execute
echo 'I am a message that nobody will read';
} catch (Exception $e) {
echo 'Exception caught: '. $e->getMessage();
}
Pomembno je opozoriti, da se izjema lahko vrže globlje, med klicem drugih metod.
Za en blok try
lahko določite več blokov catch
, če pričakujete različne vrste izjem.
Ustvarimo lahko tudi hierarhijo izjem, kjer vsak razred izjem podeduje od prejšnjega. Kot primer si oglejmo preprosto bančno aplikacijo, ki omogoča vplačila in izplačila:
class BankingException extends Exception {}
class InsufficientFundsException extends BankingException {}
class ExceededLimitException extends BankingException {}
class BankAccount
{
private int $balance = 0;
private int $dailyLimit = 1000;
public function deposit(int $amount): int
{
$this->balance += $amount;
return $this->balance;
}
public function withdraw(int $amount): int
{
if ($amount > $this->balance) {
throw new InsufficientFundsException('Not enough funds in the account.');
}
if ($amount > $this->dailyLimit) {
throw new ExceededLimitException('Daily withdrawal limit exceeded.');
}
$this->balance -= $amount;
return $this->balance;
}
}
$account = new BankAccount;
$account->deposit(500);
try {
$account->withdraw(1500);
} catch (ExceededLimitException $e) {
echo $e->getMessage();
} catch (InsufficientFundsException $e) {
echo $e->getMessage();
} catch (BankingException $e) {
echo 'An error occurred during the operation.';
}
V tem primeru je pomembno upoštevati vrstni red blokov catch
. Ker vse izjeme dedujejo od
BankingException
, bi se, če bi imeli ta blok prvi, vse izjeme ujele v njem, ne da bi koda dosegla naslednje bloke
catch
. Zato je pomembno, da so bolj specifične izjeme (tj. tiste, ki dedujejo od drugih) višje v vrstnem redu
blokov catch
kot njihove nadrejene izjeme.
Najboljše prakse
Ko obvladate osnovna načela objektno usmerjenega programiranja, se je treba osredotočiti na najboljše prakse v OOP. Te vam bodo pomagale pri pisanju kode, ki ni le funkcionalna, temveč tudi berljiva, razumljiva in jo je mogoče zlahka vzdrževati.
- Oddelitev interesov: Vsak razred mora imeti jasno opredeljeno odgovornost in mora obravnavati le eno primarno nalogo. Če razred počne preveč stvari, je morda primerno, da ga razdelite na manjše, specializirane razrede.
- Enkapsulacija: Podatki in metode morajo biti čim bolj skriti in dostopni le prek opredeljenega vmesnika. To omogoča spreminjanje notranje implementacije razreda, ne da bi to vplivalo na preostalo kodo.
- Vključevanje odvisnosti: Namesto da bi odvisnosti ustvarjali neposredno v razredu, bi jih morali “vbrizgati” od zunaj. Za globlje razumevanje tega načela priporočamo poglavja o vbrizgavanju odvisnosti.