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

Κληρονομικότητα

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

Ένα όχημα για παράδειγμα γνωρίζουμε ότι διαθέτει ρόδες για να μπορέσει να μετακινηθεί, επίσης γνωρίζουμε ότι θα έχει κάποια τελική ταχύτητα. Γενικεύουμε έτσι όλα τα οχήματα που έχουν ρόδες ως «όχημα»(vehicle). Με αυτή την γενίκευση μπορούμε να ειδικεύσουμε αργότερα σε περεταίρω «κατηγορίες», π.χ. αυτοκίνητο (car) ή μηχανάκι (motorcycle). Ένα αυτοκίνητο γνωρίζουμε πως θα έχει τέσσερις ρόδες (wheels) , ενώ ένα μηχανάκι θα έχει δυο, καθώς η τελική ταχύτητα είναι ανεξάρτητη (max speed) και για τα δυο. Έτσι προκύπτει ο παρακάτω κώδικας:

<?php
 class vehicle{
  public $wheels;
  public $final_speed;
  public $active='false';

  function __construct($w,$fs){
   $this->wheels=$w;
   $this->final_speed = $fs;
  }
  
  function start_engine(){
    $this->active = 'true';
    return true;
  }
 }
 
 class car extends vehicle{
  function __construct($fs){
   parent::__construct(4,$fs);
  }
 }

 class motorcycle extends vehicle{
  function __construct($fs){
   parent::__construct(2,$fs);
  }
  
  public function startWheelie(){
   $this->wheels=1;
   return $this->wheels;
  }
  
  public function endWheelie(){
   $this->wheels=2;
   return $this->wheels;
  }
 }
 
 $a = new car(300);
 $b = new motorcycle(150);
 
 print_r($a);
 echo "<br />";
 print_r($b);
 
 ?>

Όπως παρατηρούμε στον παραπάνω κώδικα υπάρχουν τρείς διαφορετικές κλάσεις οι οποίες όμως έχουν μια «σχέση» μεταξύ τους. Η κλάση «vehicle» είναι η κλάση-πατέρας από την οποία οι υπόλοιπες κλάσεις θα κληρονομήσουν στοιχεία. Η κλάση «car» και «motorcycle», είναι οι κλάσεις-παιδιά που κληρονομούν όλα τα στοιχεία της κλάσης-πατέρα.

  • Όταν μία κλάση κληρονομεί στοιχεία μιας άλλης κλάσης, η κλάση-παιδί ονομάζεται: υποκλάση (subclass), παραγόμενη κλάση (derived) ή θυγατρική κλάση (child), ενώ η κλάση-πατέρας: υπερκλάση (superclass), γονική κλάση (parent), ή βασική κλάση (base).
  • Για να ορίσουμε ότι μία κλάση είναι υποκλάση μιας υπερκλάσης χρησιμοποιούμε την εντολή «extends».
  • Το μέλος που βρίσκεται αριστερά από το «extends» είναι το όνομα της υποκλάσης που θέλουμε να δημιουργήσουμε και το μέλος που βρίσκεται δεξιά είναι το όνομα της υπερκλάσης που θέλουμε από αυτό να κληρονομήσει στοιχεία.
  • Μια υποκλάση κληρονομεί όλα τα στοιχεία (ιδιότητες και λειτουργίες) της υπερκλάσης τα οποία έχουν δείκτη προσβασιμότητας protected ή public, αλλά ποτέ δεν κληρονομεί τον κατασκευαστή, μπορεί όμως να έχει πρόσβαση σε αυτόν.
  • Οι υποκλάσεις μπορούν να έχουν δικές τους ιδιότητες ή λειτουργίες οι οποίες να μην έχουν καμία σχέση με την υπερκλάση που προέρχονται.
  • Μια κλάση μπορεί να κληρονομεί μόνο μια άλλη κλάση. Δεν μπορεί δηλαδή μια κλάση-παιδί να έχει κλάση-πατέρα και κλάση-μητέρα αλλά μόνο ένα από τα δυο.
  • Μια υποκλάση μπορεί να γίνει υπερκλάση μιας άλλης υποκλάσης.
  • Όταν θέλουμε μέσα από μια υποκλάση να έχουμε πρόσβαση σε ιδιότητες ή λειτουργίες μιας υπερκλάση τότε χρησιμοποιούμε το πρόθεμα «parent::»

Υπερκάλυψη

Η υπερκάλυψη είναι μια δυνατότητα των υποκλάσεων να «υπερκαλύπτουν» μια ήδη υπάρχουσα ιδιότητα ή λειτουργία σε μια υπερκλάση κάνοντας παραπάνω λειτουργίες ή προσαρμόζοντας μια ιδιότητα της.

Έτσι αν μια υποκλάση έχει μια μέθοδο με όνομα «start_engine()» η οποία με ακριβώς ίδιο όνομα υπάρχει και στην υπερκλάση, τότε για να χρησιμοποιήσουμε την μέθοδο της υπερκλάσης χρησιμοποιούμε το πρόθεμα «parent::» ενώ αν θέλουμε να χρησιμοποιήσουμε την μέθοδο της υποκλάσης που βρισκόμαστε χρησιμοποιούμε το πρόθεμα «$this->». Ας δούμε το παράδειγμα με τα οχήματα.

<?php
 class vehicle{
  public $wheels;
  public $final_speed;
  public $active='false';
  
  function __construct($w,$fs){
   $this->wheels=$w;
   $this->final_speed = $fs;
  }
  
  function start_engine(){ //αυτή είναι η μέθοδος της υπερκλάσης
    $this->active = 'true';
    return true;
  }
 }
 
 class car extends vehicle{
  public function start_engine(){
   parent::start_engine(); // καλείται η μέθοδος της υπερκλάσης
   echo "Το αμάξι πήρε μπροστά";
  }
  function __construct($fs){
   parent::__construct(4,$fs);
  }
 }
 
 $a = new car(300);
 
 $a->start_engine(); //εδώ θα γίνει χρήση της μεθόδου στο car
 echo "<br/>";
 print_r($a);
 
 ?>

Εδώ βλέπουμε πως μέσα από μια λειτουργία μιας υποκλάσης μπορούμε να καλέσουμε μια λειτουργία της υπερκλάσης της χρησιμοποιώντας ακριβώς το ίδιο όνομα. Αν δεν δημιουργούσαμε την καινούργια μέθοδο μέσα στο «car» όταν θα καλούσαμε την μέθοδο «$a->start_engine();», τότε το script θα χρησιμοποιούσε την μέθοδο που βρίσκεται στην υπερκλάση. Τώρα όμως που υπάρχει αντίστοιχη μέθοδος στην υποκλάση, καλείται αυτή. Το αν θα την συνδέσουμε με την μέθοδο της υπερκλάσης είναι θέμα δικό μας θα μπορούσαμε να μην συμπεριλάβουμε το «parent::start_engine();» και έτσι η μέθοδος να κάνει κάτι τελείως διαφορετικό από ότι έκανε η πρόγονος της. 

Στατικές Μεταβλητές

Οι στατικές (static) μεταβλητές είναι ένας τρόπος να μοιραζόμαστε μια μεταβλητή και τις αλλαγές αυτής, άμεσα με όλες τις υποκλάσεις μιας υπερκλάσης που την διαθέτει. Ας δούμε ένα σύντομο παράδειγμα χρήσης:

<?php
 class vehicle{
  static $max_speed = 120;
 }
 echo vehicle::$max_speed;
?>

Ας υποθέσουμε πως κάθε όχημα διαθέτει έναν μέγιστο όριο ταχύτητας με το οποίο μπορεί να κινηθεί στους δρόμους της Ελλάδας, και το οποίο είναι κοινό για όλα τα οχήματα. Πριν το 2010 το μέγιστο όριο ταχύτητας με βάση τον κώδικα οδικής κυκλοφορίας ήταν 120 χλμ την ώρα, ενώ με βάση κάποιες αλλαγές που πραγματοποιήθηκαν το 2011 το όριο αυτό άλλαξε σε 130 χλμ την ώρα.

Αν η μεταβλητή που αντικατοπτρίζει αυτόν τον περιορισμό ήταν διαφορετική για κάθε όχημα με βάση την στιγμή της δημιουργίας του τότε όλα τα οχήματα που είχαν δημιουργηθεί από το 2010 και πίσω θα είχαν μέγιστο όριο ταχύτητας τα 120 χλμ/ώρα ενώ όλα τα υπόλοιπα που δημιουργήθηκαν από το 2011 και μετά θα είχαν διαφορετικό όριο ταχύτητας. Οι στατικές μεταβλητές προσπαθούν να επιλύσουν αυτό το πρόβλημα με την άμεση ισχύ της μεταβλητής σε όλες τις κλάσεις την στιγμή ακριβώς που αλλάζει. Έτσι η αλλαγή σε 130 χλμ/ώρα επηρεάζει όλα τα οχήματα ανεξαιρέτως το πότε δημιουργήθηκαν.

<?php
 class vehicle{
  public $wheels;
  public $final_speed;
  public $active='false';
  public static $max_speed=120;
  
  function __construct($w,$fs){
   $this->wheels=$w;
   $this->final_speed = $fs;
  }
  
  function get_maxspeed(){
   return static::$max_speed;
  }
  
  function start_engine(){ //αυτή είναι η μέθοδος της υπερκλάσης
    $this->active = 'true';
    return true;
  }
 }
 
 class car extends vehicle{
  function __construct($fs){
   parent::__construct(4,$fs);
  }
 }
 
  class motorcycle extends vehicle{
  function __construct($fs){
   parent::__construct(2,$fs);
  }
 }
 
 $a = new car(300);
 $b = new motorcycle(150);
 
 echo "C:".$a->get_maxspeed()." - M:".$b->get_maxspeed()."<br/>"; //τύπωση μέγιστου ορίου 

 vehicle::$max_speed=130; //αλλαγή μέγιστου ορίου

 echo "C:".$a->get_maxspeed()." - M:".$b->get_maxspeed()."<br/>"; //τύπωση ξανά 
?>

Όπως βλέπουμε στον κώδικα η λέξη «static» είναι η λέξη κλειδί στην προσπέλαση στατικών μεταβλητών ή και μεθόδων. Τόσο οι στατικές μεταβλητές όσο και οι στατικές μέθοδοι λειτουργούν αυτόνομα και χωρίς να χρειάζεται να έχει δημιουργηθεί κάποιο αντικείμενο για να «τρέξουν». Ο τρόπος με τον οποίο μπορούμε να καλέσουμε μια ιδιότητα ή μια μέθοδο χωρίς να έχει δημιουργηθεί αντικείμενο είναι «όνομα_κλάσης::όνομα_μεθόδου(Ορίσματα);», ως αντικατάσταση στο συγκεκριμένο παράδειγμα χρησιμοποιούμε μια μέθοδο για να αποκτήσουμε πρόσβαση στην στατική ιδιότητα. Για να την χρησιμοποιήσουμε όμως χρειαζόμαστε να την προσπελάσουμε ως στατική «static::$max_speed;».

Διαρκές μεταβλητές

Μια διαρκής μεταβλητή χρησιμοποιείται με παρόμοιο τρόπο όπως η στατική μόνο που δεν μπορεί να είναι private ή protected αλλά μόνο public εξ’ ορισμού.

Αυτού του είδους οι μεταβλητές χρησιμοποιούνται για να δηλωθούν τιμές οι οποίες δεν αλλάζουν όπως για παράδειγμα η περίμετρος ενός κύκλου. Επίσης οι διαρκές μεταβλητές δεν έχουν το σύμβολο «$» του δολαρίου και επομένως δεν μπορούν να χρησιμοποιηθούν σε συνθήκες.

<?php
 class car{
  const pi=3.14;
 }
 
 echo car::pi;
?>

Όπως βλέπουμε παραπάνω η χρήση της διαρκής μεταβλητής έξω από την κλάση γίνεται με το όνομα του αντικειμένου το πρόθεμα «::» και το όνομα της διαρκής μεταβλητής δηλ «car::pi». Όταν θέλουμε να προσπελάσουμε την μεταβλητή μέσα από την ίδια την κλάση τότε χρησιμοποιούμε το πρόθεμα «self::».

<?php
 class car{
  const pi=3.14;
  
  public function test(){
   echo self::pi;
  }
 }
 
 $c = new car();
 
 echo $c::pi;
 echo "<br/>";
 echo $c->test();
?>

Εδώ πρέπει να σημειώσουμε πως μια υποκλάση κληρονομεί τις διαρκές μεταβλητές τις υπερκλάσης της.

<?php
 class vehicle{
  const pi=3.14;
 }
 
 class car extends vehicle{
  public function test(){
   echo parent::pi;
  }
 }
 
 $c = new car();
 echo $c->test();
?>

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