Public, privat, protejat

Public, privat, protejat

M-a rugat cineva de curând să-i explic cum stă treaba cu vizibilitatea în OOP, mai ales în contextul WP. Cum treaba asta poate fi destul de tricky de înțeles, am zis că ar fi util să scriu un scurt articol pe blog.

Ca să înțelegi ce este vizibilitatea, trebuie să înțelegi OOP. Mai exact primul O: Obiectele și relația dintre ele.

Un limbaj cu clasă§

La modul cel mai simplist, o clasă este o adunătură de…ceva-uri. Funcții (numite metode), variabile sau constante. Conținutul clasei este absolut irelevant și poate consta în lucruri cu sau fără legătură între ele:

class Foo {
  public $serverID = 100;
  const DB_VERSION = null;

  public function getUser()
  {}

  public function getHttpRequest()
  {}
}

Ca să folosim clasa de mai sus este nevoie să o instanțiem. Instanța asta este, de fapt, un obiect:

$foo = new Foo; // <<< aici instanțiem o clasă
$foo // <<< aici avem un obiect

Dar experiența ne-a învățat că, în mod ideal, conținutul claselor este format din ceva-uri cu legătură.

Animalul§

O să mergem totuși pe exemplul tradițional când vine vorba de explicarea claselor: animalele. Este un exemplu care, la fel ca și cel cu mașinile, mi se părea extrem de confuz când învățam OOP, dar, ulterior, am ajuns la concluzia că este o metaforă potrivită. Deci bear with me. 🐻

class Animal {
  public function mănâncă()
  {}

  public function dormi()
  {}
}

Ce este Vizibilitatea?§

Pe scurt: cum se vede treaba „din afară”. Pe lung este ceva mai… lung, pentru că trebuie să definim ce înseamnă „în afară”, respectiv „înăuntru”.

Afară§

Există două feluri de „afară”: la nivel de instanță și la nivel de moștenitor.

Instanța§

Așa cum am spus mai sus, instanța unei clase este definită prin cuvântul cheie new urmat de numele clasei:

$animal = new Animal;

În această instanță putem apela orice metode definită în clasă și marcate ca publice:

$animal->mănâncă();
$animal->dormi();

Moștenirea§

Una din caracteristicile OOP este moștenirea (inheritance). Asta ne permite să extindem o clasă, astfel încât să adăugăm funcționalități noi. De exemplu, putem avea un Câine și o Pisică:

class Câine extends Animal 
{
  public $vârsta = 2;

  public function latră()
  {
    $this->dormi();
  }
}

$câine = new Câine;

Spun metode, dar mă refer de fapt la toate ceva-urile de care am zis mai sus: metode sau variabile

În situația de față, o clasă își expune toate metodele moștenitorului, astfel încât moștenitorul are acces la acele metode ca și cum ar fi ale lui.

Înăuntru§

Dacă asta înseamnă afară, ce înseamnă… înăuntru? Păi… fix invers: orice este accesibil doar în clasă. Și poate fi accesibil doar în clasa în care este definit SAU și în clasele moștenitoare.

Prin urmare, putem spune că avem trei posibilități când vine vorba de vizibilitate:

  1. public: când este vizibil și din interior și din exterior;
  2. protected: când este vizibil doar în interiorul clasei în care este definit ȘI în moștenitori;
  3. privat: când este vizibil doar în interiorul clasei în care este definit.

Now you see me§

Toată situația poate fi foarte confuză când ești la început. Când să folosești și ce?

Ca regulă generală, mergi pe ideea: este apelat vreodată din afară? Atunci e public. Altfel e privat sau protected.

Public§

În PHP, vizibilitatea implicită este public, deci keyword-ul este opțional (dar recomandat să se folosească, pentru a sublinia intenția de a face acea metodă publică).

Facem publice fiecare metodă ce poate fi apelată din exterior:

$câine = new Câine;
$câine->dormi();
echo $câine->vârsta;

Un caz mai special: callbacks§

Atenție însă, când ai nevoie de o metodă într-un callback (fie că-i array_map sau add_filter sau altceva asemănător), metoda trebuie să fie publică, indiferent că-i folosită în interiorul clasei sau nu:

  public function latră()
  {
    array_map([$this, 'foo'], $array);
  }

  public function foo(){}

este, cumva, echivalent cu:

  public function latră()
  {
    array_map($instanta->foo(), $array);
  }

  public function foo(){}

Privat sau Protected?§

Dacă am stabilit când folosim public, când le folosim pe celelalte? Am zis mai sus: atunci când sunt folosite în interior. Dar… ce înseamnă asta mai exact?

Privat§

Te poți gândi la metodele private ca la ceva ce ține strict de clasa aia și de nimic altceva. Luând exemplul câinelui de mai sus, putem extinde clasa și mai mult, implementând

class Doberman extends Câine 
{
  private function mârâie(){}

  public function latră()
  {
    $this->mârâie();
  }
}

class Collie extends Câine 
{
  private function mârâie(){}

  public function latră()
  {
    $this->mârâie();
  }
}

Putem spune că un câine poate lătra la comandă, dar modul în care o face este diferit pentru fiecare rasă.

Într-un exemplu real, putem avea ceva de genul:

class LatestItems {
  protected function getLatestPosts($type)
  {
    return []; // posts
  }
}

class LatestPosts extends LatestItems
{
  private function capitalizeTitles($items){}

  public function getLatestPosts() {
    $items = $this->getLatestPosts('blog');
    $items = $this->capitalizeTitles($items);
  }
}

class LatestImages extends LatestItems
{
  private function resizeImages($items) {}

  public function getLatestItems() {
    $items = $this->getLatestPosts('images');
    $items = $this->resizeImages($items);
  }
}

După cum poți observa, în clasa LatestImages avem o metodă resizeImages ce nu are ce căuta în clasa LatestPosts, fiind o funcționalitate internă.

În plus, nu avem nevoie să facem nimic de genul:

$img = new LatestImages;
$img->resizeImages();

Deci nu avem motive să facem acea metodă altfel decât privată.

În plus, un avantaj enorm al marcării unei metode ca fiind privată este acela de a zice programatorului viitor (indiferent că ești tu sau altcineva): „poți schimba fără grijă metoda asta, nu va afecta alte clase!”

Protected§

Dacă am învățat ce înseamnă private, oare la ce ne ajută protected? Tot în exemplul de mai sus, în clasa părinte (LatestItems), observi că avem o metodă numită getLatestPosts. Așa cum se vede, această metodă este apelată de moștenitori, dar nu din afară.

// este apelată așa:
$this->getLatestPosts();

// nu este apelată așa:
$img->getLatestPosts();

__construct() e public!§

Am văzut azi pe facebook o nelămurire: de ce __construct e public?

Explicația cea mai simplă cred că este că următoarele două linii sunt echivalente:

$azorică = new Câine;
$grivei  = Câine->__construct(); 

Desigur, a doua linie nu merge, dar cred că ajută în a vizualiza mai bine de ce este public.

Poate fi __construct altfel decât public? DA! Există un design pattern numit Singleton – dar ăsta e un subiect pentru un alt post.

De ce?§

Probabil la momentul ăsta te întrebi: de ce atâta bătaie de cap? De ce nu sunt toate publice și gata?

OOP este mai mult decât „hai să punem toate astea la grămadă”. Este despre limitarea responsabilităților, despre organizarea codului, despre citibilitatea codului (readability) și despre încapsulare.

Un Comentariu

Adaugă un comentariurăspuns pentru

Poți adăuga bucăți de cod folosind [code]codul tău aici[/code], [js][/js], [php][/php] etc.

Link-urile în context sunt binevenite. Comentariile fără nume/email valid sunt șterse.
PS: Comentariul NU este editabil.

Acest site folosește Akismet pentru a reduce spamul. Află cum sunt procesate datele comentariilor tale.

Site-ul blog.iamntz.com utilizează cookie-uri. Continuarea navigării presupune acceptarea lor. Mai multe informații.

windows apple dropbox facebook twitter