My blog

posts

What interfaces are

Un concept cheie in lumea programarii orientate pe obiecte este acela de interfete. Dar ce sunt aceste interfete si la ce ne ajuta pe noi programatorii sa construim aplicatii mai bune? Gandeste-te la interfete ca la un contract pe care mai multi oameni il semneaza si se obliga sa respecte normele din contract. 

Ca sa luam un exemplu, sa ne imaginam ca avem o interfata Vehicle. Acum, vehiculele pot fi de multe tipuri, dar toate au functionalitati comune, precum: claxoneaza, porneste stergatoarele etc. Ei bine, toate aceste functionalitati le putem preciza in interfata Vehicle (in contract), pentru a ne asigura ca toate  masinile care vor implementa aceasta interfata au aceste functionalitati/dotari.

Deci, sa zicem ca avem o interfata Vehicle care are in ea o functie startEngine.

Interface Vehicle{
	public void startEngine();
}
Dupa cum observi in interfata Vehicle am declarat doar antetul functiei startEngine. Asta pentru ca masinile au metode diferite de pornire a motorului, o masina pe dissel va porni diferit de una electrica. Iar acesta este o regula de tinut minte: in interiorul interfetelor declaram doar antetul functiilor(creem conditiile contractului).

Acum, hai sa facem doua clase Audi si Tesla sa implementeze interfata Vehicle. Atentie, am folosit termenul “sa implementeze”, deci o clasa poate implementa o interfata, nu o poate extinde ca la clasele abstracte, o implementeaza. 
class Audi implements Vehicle{

}

class Tesla implements Vehicle{

}
Acum, avand in vedere ca am implementat interfata (am semnat contractul), clasele noastre trebuie contina functia inpusa de interfata, in caz contrar vom primi o eroare. Deci hai sa facem asta. 
class Audi implements Vehicle{
  public void function startVehicle(){
     return “vrum”;
  } 
}

class Tesla implements Vehicle{
   public void function startVehicle(){
      return “no noise”;
   }
}
Dupa cum vezi, clasele care implementeaza interfata sunt cele ce definesc logica din spatele functiilor inpuse de aceasta. Astfel ca,  putem avea logici diferite de a porni masina dar ne vom conforma aceluiasi contract. 

Acum probabil te intrebi : “Ok, si cu ce ma ajuta pe mine asta?”. Hai sa luam un exemplu mai real. Sa zicem ca avem o interfata Logger si doua clase LogToFile si LogToDatabase.
interface Logger{}

class LogToFile {

   public function execute($message){
     return “logging to file $message”;
   }
}

class LogToDatabaser{

   public function execute($message){
     return “Logging to database $message”;
   }
}
Dupa cum observi, clasele nu adera la interfata Logger si am facut acest lucru in tocmai ca sa poti intelege de ce avem nevoie sa folosim o interfata. Acum, sa presupunem ca avem un controller UserController, asta ar fi asemanator cu un HttpController. Sa presupunem ca atunci cand functia show este apelata vrem sa inregistram numele utilizatorului intr-un fisier. 
class UserController {
   public function show(){
	$user = “Claudiu”;
   }
}
Insa cum am putea face acest lucru? Am putea crea o noua instanta de clasa LogToFile, insa asta este considerata o practica gresita, deoarece nu putem fi siguri ca acea clasa chiar exista. Asa ne vom folosi de constructorul clasei UserController pentru a ne face legatura cu clasa LogToFile si vom avea codul urmator.
class UserController{
   protected $logger;
   
   public __construct(LogToFile $logger){
     $this->logger = $logger;
   }

   public function show(){
	$user = “Claudiu”;
	$this->logger->execute($user);
   }
}
Cu toate astea, ce am putea face daca am vrea sa inregistram userul in baza de date? Cum am proceda in cazul acela? Ei bine, problema este ca noi am setat deja o clasa pentru variabila $logger peste tot. Si hai sa ne gandim ca mai multe clase din aplicatia ta se folosesc de asta. Cel mai probabil asa va fi. 

Ok, si ce putem face acum? Ei bine, ai putea sa mergi si sa faci replace la toate locurile in care gasesti acea clasa implementata sau sa ne gandim mai bine la care ar fi problema. Problema este ca am fost prea specifici, sau in alte cuvinte am considerat o singura varianta corecta, nu ne-am gandit pe termen lung si n-am luat in considerare ca aceasta poate necesita o schimbare.

Pai si care ar fi solutia? Solutia ar fi sa programam pentru o interfata, nu pentru o implementare. Sa nu ne asumam o singura varianta, ci sa lasam loc si pentru altele. Aici ne vin in ajutor interfetele. Astfel ca, va trebui sa facem cele doua clase LogToFile si LogToDatabase sa implementeze interfata Logger care va lua rolul de contract. Iar in constructorul controllerului vom specifica interfata, nu clasa LogToFile.
interface Logger{
  public function execute($message);
}

class LogToFile {
   public function execute($message){
	return “logging to file $message”;
   }
}

class LogToDatabaser{
    public function execute($message){
	return “Logging to database $message”;
    }
}

class UserController{
   protected $logger;
   public __construct(Logger $logger){
      $this->logger = $logger;
   }

   public function show(){
      $user = “Claudiu”;
      $this->logger->execute($user);
   }
}
Astfel, vom putea folosi ambele modalitati de inregistrare a userilor. Prin urmare clasa UserController nu va fi nevoita sa stie cine inregistreaza userul. Ea va sti doar ca exista o clasa care implementeaza interfata Logger ce o poate ajuta sa inregistreze userul intr-un fisier sau intr-o baza de date.

In concluzie, interfetele sunt un concept puternic in lumea programatorilor, ce ne poate ajuta sa scriem cod mai sustenabil si mai eficient. De asemenea, reprezinta un API public pentru toate clasele din aplicatia ta, astfel ca orice clasa poate avea o implementare a acestei interfete sau a mai multora. 

Stiu ca acesta a fost un articol foarte lung,dar sper ca nu te-a descurajat ci din contra ca te-a motivat sa intelegi cat de bine posibil acest concept pentru a-l putea utiliza in viitor in aplicatiile tale. Daca ti-a placut acesta articol nu uita sa imi lasi un comentariu, iar daca nu ti-ar placut, astept un sfat pentru a-mi putea imbunatati calitatea continutului de pe blog. Pana data viitoare, be smart and code simple!




0 Comments

  • Be the first one who leaves a comment!

Add a comment