En la entrega anterior dejamos algunos temas en el tintero. Entre ellos estaba cómo funcionan las propiedades public, private y protected cuando hay clases heredadas de por medio. También quedó pendiente el overriding. En la entrega de hoy voy a hablar de ambos.
Herencia y public, private y protected
Ya comenté que a las propiedades y métodos public se puede acceder desde fuera de la clase y a las private solo se puede acceder desde dentro de la clase.
Vamos a recuperar el código de la clase noticia:
class Noticia { private $titulo; private $cuerpo; public function getTitulo() { return $this->titulo; } public function setTitulo($titulo) { $this->titulo = $titulo; } public function getCuerpo() { return $this->cuerpo; } public function setCuerpo($cuerpo) { $this->cuerpo = $cuerpo; } }
y de la clase NoticiaFutbol:
class NoticiaFutbol extends Noticia { private $equipo1; private $equipo2; private $resultado; public function getEquipo1() { return $this->equipo1; } public function setEquipo1($equipo1) { $this->equipo1 = $equipo1; } public function getEquipo2() { return $this->equipo2; } public function setEquipo2($equipo2) { $this->equipo2 = $equipo2; } public function getResultado() { return $this->resultado; } public function setResultado($resultado) { $this->resultado = $resultado; } }
Supongamos que en la clase NoticaFutbol necesitamos una función que, usando el título, los equipos participantes y el resultado, cree un título completo. Algo así “Gran partidazo – Algarrobo FC / RC Vesarrubia (1-0)”
A esta función vamos a llamarla generarTituloCompleto y va a ir dentro de la clase NoticiaFutbol:
public function generarTituloCompleto() { return $this->getTitulo() . " - " . $this->equipo1 . " / " . $this->equipo2 . " (" . $this->resultado . ")"; }
Esta función la usaríamos así:
$noticia = new NoticiaFutbol(); $noticia->setTitulo("Gran partidazo"); $noticia->setCuerpo("Jugaron 90 minutos y hubo goles"); $noticia->setEquipo1("Algarrobo FC"); $noticia->setEquipo2("RC Vesarrubia"); $noticia->setResultado("1-0"); echo $noticia->generarTituloCompleto() . "\n";
Y el resultado sería:
Gran partidazo - Algarrobo FC / RC Vesarrubia (1-0)
Todo ha ido a la perfección porque los métodos son públicos. Pero ¿qué hubiera pasado si la función getTitulo hubiese sido privada?
private function getTitulo() { return $this->titulo; }
El resultado ahora sería un error:
PHP Fatal error: Call to private method Noticia::getTitulo() from context 'NoticiaFutbol'
Vaya, parece que no podemos acceder a él desde la clase hijo. Claro, a una propiedad o método private solo se puede acceder desde la clase que la definió. Entonces ¿qué hacemos?
¿Recuerdas que comenté algo sobre propiedades y métodos protected? Pues son precisamente para esto. A los miembros protected solo se puede acceder desde la clase donde están definidas o desde las clases heredadas.
Si cambiamos el método a protected:
protected function getTitulo() { return $this->titulo; }
Como puedes comprobar getTitulo es accesible desde la clase hijo pero sigue siendo inaccesible desde el “exterior”. Prueba a llamarla directamente desde el objeto:
echo $noticia->getTitulo();
verás este error:
PHP Fatal error: Call to protected method Noticia::getTitulo() ...
Resumen
A las propiedades y métodos public de una clase se puede acceder desde la propia clase, desde clases heredadas y desde el “exterior”.
A las que son private solo se puede acceder desde esa propia clase.
Y si son protected podemos acceder solo desde la propia clase y desde las clases heredadas.
Overriding
Ahora imagina que quien ha solicitado el desarrollo de la aplicación te dice que la función getTitulo debería devolver siempre un título compuesto y no solo el título de la noticia (al estilo de lo que hace la función generarTituloCompleto).
En la clase padre definimos una función getTitulo genérica que devuelve solo el título. Esto tiene que ser así porque la clase padre no sabe cómo va a ser la clase hija (vamos, igual que sucede en la realidad entre padres e hijos).
Luego, en cada subclase volvemos a definir la función getTitulo:
public function getTitulo() { return $this->titulo . " - " . $this->equipo1 . " / " . $this->equipo2 . " (" . $this->resultado . ")"; }
Esto que hemos hecho se llama, en inglés, override.
Un detalle importante, para poder acceder a la propiedad título de la clase Noticia tendríamos que redefinirla como protected:
protected $titulo;
Así que, cuando llamamos a un método se le busca primero en la clase hijo. Si no existe se busca en la clase padre. Si el método no existe ni en la clase padre ni en la subclase se produce un error.
Para verlo vamos a recuperar el código de la clase NoticiaCiclismo:
class NoticiaCiclismo extends Noticia { private $ganadorEtapa; private $ganadorGeneral; public function getGanadorEtapa() { return $this->ganadorEtapa; } public function setGanadorEtapa($ganadorEtapa) { $this->ganadorEtapa = $ganadorEtapa; } public function getGanadorGeneral() { return $this->ganadorGeneral; } public function setGanadorGeneral($ganadorGeneral) { $this->ganadorGeneral = $ganadorGeneral; } }
Y usamos esta clase así:
$noticiaNormal = new NoticiaCiclismo(); $noticiaNormal->setTitulo("Noticia de ciclismo con título normal y corriente"); echo $noticiaNormal->getTitulo() . "\n";
Aquí el resultado sería:
Noticia de ciclismo con título normal y corriente
La clase NoticiaCiclismo no tiene un método llamado getTitulo así que se usa el de la clase padre.
Próxima entrega
En el próximo artículo hablaré sobre las clases abstractas, que es un tema que suele confundir mucho a la gente.