Aprenda POO PHP

  • Página Inicial
  • Contato!
  • Tudo sobre POO PHP Parte 1!
  • Tudo sobre POO PHP Parte 2!
  • Tudo sobre POO PHP Parte 3!
  • Tudo sobre POO PHP Parte 4!
  • Tudo sobre POO PHP Parte 5!
  • Tudo sobre POO PHP Parte 6!
  • Tudo sobre POO PHP Parte 7!
  • Tudo sobre POO PHP Parte 5

    Herança - Parte 2

    Sabendo o que é uma herança em POO, onde uma classe herda coisas de outra classe. A partir daí, podemos criar uma "árvore" de heranças, onde uma classe herda coisas de outras, veja o diagrama da árvore abaixo:

    Árvore

    No caso, o AAA herda características de AA (mãe), e esta herda de A (avó). AAA tem tudo que tem AA e A. E AA tem tudo de A, e assim por diante. No caso acima, o ABAA herda das clases mãe, avó e bisavó. Em outras palavras, toda subclasse herda todos os conteúdos de suas ancentrais (mesmo se não tiverem totalmente disponíveis com visibilidades privadas e protegidas).

    Toda classe que tem filhas é progenitora, independente dela ser a principal ou não. Superclasse sempre é a que está acima, e subclasse é sempre o que está abaixo (no exemplo acima, ABA é superclasse de ABAA e também subclasse de AB).

    Como uma "árvore" mesmo de cabeça pra baixo, consideraremos que as classes que não tem filhas são como folhas, e a principal é a raiz. Vamos considerar que as filhas não são consideradas descendentes, e nem mães ancenstrais, vamos considerar a partir de netas/avós.

    Quando corremos uma árvore de cima pra baixo, fazemos a especialização, de baixo pra cima é a generalização.

    Veja o que faremos em PHP abaixo:

    Heranças

    Vamos usar a classe Pessoa como antes, mas deixe ela abstrata antes (colocando abstract class Pessoa), e sem o construtor. A classe abstrata não pode virar um objeto (ser instanciada), apenas ser usada em heranças, se tentarmos criar $p1 = new Pessoa() agora, ela dará erro.

    Crie a classe Visitante.php assim:

    
    require_once "Pessoa.php";
    
    class Visitante extends Pessoa {
        
    }
    
    

    Agora tente cria o objeto, assim:

    
    require_once "Visitante.php";
    
    $v1 = new Visitante();
    
    $v1->setNome("Juvenal");
    $v1->setIdade(33);
    $v1->setSexo("M");
    
    print_r($v1);
    
    

    Isso é o que chamamos de herança pobre, quando uma classe (classe filha Visitante) traz todas as heranças de outra classe (classe mãe Pessoa), mas não adiciona nada mais, mas pode virar objeto pela filha não ser abstrata. Pode ver que ele funciona normalmente, mas se usarmos Pessoa em require_once e no objeto Pessoa(), ele dará erro porque só pode ser usado para heranças.

    Agora vamos usar a classe Aluno novamente, que terá a herança para diferença, por acrescentar algumas coisas. Substitua o CancelarMatr() por PagarMensal() e crie o getter e setter dele, assim:

    
    public function pagarMensal() {
        echo "<p>Pagando Mensalidade do aluno $this->nome.</p>";
    }
    
    

    PS: Pode ser necessário ir na classe Pessoa e colocar os atributos de privados para protegidos (aí você verá a diferença entre ambos).

    E no index, crie um objeto com Aluno, assim:

    
    require_once "Aluno.php";
        
    $a1 = new Aluno();
        
    $a1->setNome("Sérgio");
    $a1->setIdade(16);
    $a1->setSexo("M");
    $a1->setMatr(1111);
    $a1->setCurso("Informática");
    
    $a1->pagarMensal();
    
    print_r($a1);
    
    

    Podem ver aí que ele herdou as características da superclasse Pessoa, mas ganhou novas características. Isso é a herança de diferença.

    Agora criaremos uma classe filha de Aluno (e neta de Pessoa), que herdará características das classes mães e avós.

    Esse é o código da classe Bolsista.php (que é um tipo específico de aluno):

    
    require_once "Aluno.php";
    
    class Bolsista extends Aluno {
        private $bolsa;
        
        public function renovarBolsa() {
            echo "<p>Bolsa Renovada!</p>";
        }
        
        // Esse método substituirá o da classe Aluno somente nesse caso:
        public function pagarMensal() {
            echo "<p>$this->nome é bolsista, então paga com desconto!</p>;
        }
        
        public function getBolsa() {
            return $this->bolsa;
        }
    
        public function setBolsa($bolsa) {
            $this->bolsa = $bolsa;
        }
    }
    
    

    E no index colocamos algo assim:

    
    require_once "Bolsista.php";
        
    $b1 = new Bolsista();
        
    $b1->setNome("Jubileu");
    $b1->setIdade(17);
    $b1->setSexo("M");
    $b1->setMatr(1112);
    $b1->setCurso("Administrador");
    $b1->setBolsa(12.5);
    
    $b1->pagarMensal();
    $b1->renovarBolsa();
    
    print_r($b1);
    
    

    Pode ver que a classe Bolsista herda coisas do Aluno, que herda de Pessoa.

    PS: Caso não queira que um método seja sobreposto (substituído) por um descendente, coloque final antes do function na classe mãe (no caso, Aluno.php), assim:

    
    public final function pagarMensal() {
        echo "<p>Pagando Mensalidade do aluno $this->nome.</p>";
    }
    
    

    O mesmo vale para classes:

    
    final class Aluno extends Pessoa {
    
    }
    
    

    Mas em ambos os casos, o final dará erro caso tentemos substituí-los nas subclasses. Por isso só use o final em classes folhas (que não terão filhas). O final não é usado em atributos no PHP (nesse caso use const e escreva o atributo sem cifrão, nesse caso deve ser inicializada, como por exemplo const CONSTANTE = "Dados", para exibir use ::, como por exemplo $objeto::CONSTANTE, e quando dentro da mesma classe, self::CONSTANTE).

    Façam também, como exercício, as classes Professor, Técnico e as especializações de ambos.

    Polimorfismo em PHP - Parte 1

    Lembrando dos três pilares da POO, Encapsulamento, Herança e Polimorfismo (representados pelas letras EHP). Vamos tratar do último pilar que é o polimorfismo.

    O nome é mais difícil do que o significado, pois polimorfismo significa "muitas formas". O polimorfismo permite que um mesmo nome represente vários comportamentos.

    Para entendermos um caso, vamos supor um caso de quando acordamos, tem dia que acordamos rápido pra trabalhar, toma café, mas no Domingo demoramos mais, acordamos mais tarde e etc., em outras palavras, são várias formas de executar uma mesma função (no caso, um suposto método Acordar).

    Para entendermos, um método tem "assinaturas", que consiste na quantidade e os tipos de parâmetros. Todo método podem receber ou não parâmetros, a quantidade deles identificam sua assinatura. Elas vem de heranças de classes.

    Os dois tipos mais usados de polimorfismo são os de sobreposição e o de sobrecarga.

    No exemplo, usaremos o polimorfismo de sobreposição (Override), que substitui um método que vem da classe mãe na classe filha.

    Em outras palavras, sobreposição é o mesmo método e assinatura em classes diferentes.

    Veja o diagrama que utilizaremos aqui:

    Polimorfismo

    Crie a classe Animal.php, com esse código:

    
    abstract class Animal {
        protected $peso;
        protected $idade;
        protected $membros;
        
        public abstract function locomover();
        public abstract function alimentar();
        public abstract function emitirSom();
        
        public function getPeso() {
            return $this->peso;
        }
    
        public function getIdade() {
            return $this->idade;
        }
    
        public function getMembros() {
            return $this->membros;
        }
    
        public function setPeso($peso) {
            $this->peso = $peso;
        }
    
        public function setIdade($idade) {
            $this->idade = $idade;
        }
    
        public function setMembros($membros) {
            $this->membros = $membros;
        }
    }
    
    

    PS: Teremos que colocar os métodos e a classe como abstratos, que significa que o método não será desenvolvido na classe Animal. Como acontecia com interfaces, é como um aviso pra essa classe que existe um método, mas ela não precisa e nem sabe como o código funciona na outra classe, apenas esta que tem que implementar (executar), lembrando que o abstract só vem indicado depois da visibilidade e antes de qualquer outra indicação (em classes abstratas sempre é necessário colocar abstract, as interfaces não precisam disso). Toda classe com algum método abstrato deverá também ser declarada abstrata. Lembrando que não é possível colocar métodos estáticos como abstratos, nem construtores ou destrutores, e pelo mesmo motivo, não se pode colocar os mesmos em interfaces. Nem toda classe abstrata precisa ter métodos abstratos, mas os métodos abstratos só podem ser usados em classes também abstratas.

    Crie a classe Mamifero.php:

    
    require_once "Animal.php";
    
    class Mamifero extends Animal {
        private $corPelo;
        
        public function alimentar() {
            echo "<p>Mamando</p>";
        }
        
        public function emitirSom() {
            echo "<p>Som de Mamífero</p>";
        }
        
        public function locomover() {
            echo "<p>Correndo</p>";
        }  
        
        public function getCorPelo() {
            return $this->corPelo;
        }
    
        public function setCorPelo($corPelo) {
            $this->corPelo = $corPelo;
        }
    }
    
    

    PS: No PHP não existe o @Override antes dos métodos sobrepostos.

    O de Réptil.php:

    
    require_once "Animal.php";
    
    class Reptil extends Animal {
        private $corEscama;
        
        public function alimentar() {
            echo "<p>Comendo Vegetais</p>";
        }
    
        public function emitirSom() {
            echo "<p>Som de Réptil</p>";
        }
    
        public function locomover() {
            echo "<p>Rastejando</p>";
        }
        
        public function getCorEscama() {
            return $this->corEscama;
        }
    
        public function setCorEscama($corEscama) {
            $this->corEscama = $corEscama;
        }
    }
    
    

    A de Peixe.php:

    
    require_once "Animal.php";
    
    class Peixe extends Animal {
        private $corEscama;
        
        public function alimentar() {
            echo "<p>Comendo Substâncias</p>";
        }
    
        public function emitirSom() {
            echo "<p>Peixe não faz Som</p>";
        }
    
        public function locomover() {
            echo "<p>Nadando</p>";
        }
        
        public function getCorEscama() {
            return $this->corEscama;
        }
    
        public function setCorEscama($corEscama) {
            $this->corEscama = $corEscama;
        }
    }
    
    

    E a de Ave.php:

    
    require_once "Animal.php";
    
    class Ave extends Animal {
        private $corPena;
        
        public function alimentar() {
            echo "<p>Comendo Frutas</p>";
        }
    
        public function emitirSom() {
            echo "<p>Som de Ave</p>";
        }
    
        public function locomover() {
            echo "<p>Voando</p>";
        }
        
        public function fazerNinho() {
            echo "<p>Construindo Ninho</p>";
        }
        
        public function getCorPena() {
            return $this->corPena;
        }
    
        public function setCorPena($corPena) {
            $this->corPena = $corPena;
        }
    }
    
    

    E no index, crie alguns objetos, assim:

    
    require_once "Mamifero.php";
    require_once "Reptil.php";
    require_once "Peixe.php";
    require_once "Ave.php";
    
    // $a = new Animal(); // Dará erro por ser abstrata.
    
    $m = new Mamifero();
    
    $m->setPeso(33.5);
    $m->locomover();
    
    $a = new Ave();
    
    $a->locomover();
    
    

    Esse é o conceito básico de polimorfismo de sobreposição. Como o locomover da classe Animal é abstrato, as classes filhas dele contém a mesma assinatura vindas de Animal (classe mãe), mas executam de formas diferentes. O uso de interfaces também é semelhante às classes abstratas e também é considerado um tipo de polimorfismo.

    PS: O tipo também pode ser o da classe pai (abstrata ou não), assim como acontecia com as interfaces, mas a implementação não, em polimorfismo se usa muito isso (na verdade isso seria o polimorfismo de inclusão, seja com classe pai ou interface), mas em PHP não tem tipagem, portanto não tem isso na prática.

    Mas vamos continuar o exemplo, criando animais específicos para as classes Ave, Mamifero, Peixe e Reptil. Veja o diagrama:

    Polimorfismo 2

    Veja por exemplo, a classe Canguru.php:

    
    require_once "Mamifero.php";
    
    class Canguru extends Mamifero {
        public function locomover() {
            echo "<p>Saltando</p>";
        }
    }
    
    

    A classe Cachorro:

    
    require_once "Mamifero.php";
    
    class Cachorro extends Mamifero {
        public function emitirSom() {
            echo "<p>Au! Au! Au!</p>";
        }
    }
    
    

    E a classe Tartaruga:

    
    require_once "Reptil.php";
    
    class Tartaruga extends Reptil {
        public function locomover() {
            echo "<p>Andando bem Devagar</p>";
        }
    }
    
    

    No exemplo acima, podemos já colocar isso no index:

    
    require_once "Mamifero.php";
    require_once "Reptil.php";
    require_once "Peixe.php";
    require_once "Ave.php";
    require_once "Canguru.php";
    require_once "Cachorro.php";
    require_once "Tartaruga.php";
    
    // $a = new Animal(); // Dará erro por ser abstrata.
    
    $m = new Mamifero();
    $m->locomover();
    $m->emitirSom();
    
    $c = new Canguru();
    $c->locomover();
    
    $d = new Cachorro();
    $d->emitirSom();
    
    $r = new Reptil();
    $r->locomover();
    
    $t = new Tartaruga();
    $t->locomover();
    
    

    No polimorfismo, podemos sobreescrever um método numa subclasse caso ele tenha a mesma assinatura (por isso, no polimorfismo de sobreposição a herança é necessária).

    Termine as outras classes, que serão usadas na próxima aula.

    PS: Construtores e destrutores também podem ser sobrepostos, só que no caso de construtores usamos o parent::__construct() dentro do construtor da classe filha para chamar o construtor da classe pai. E o mesmo vale pros destrutores, usando parent::__destruct(), mas neste caso, não é obrigatório. E métodos e atributos estáticos de classes abstratas podem ser acessados, desde que estes sejam públicos e também não sejam abstratos, basta colocar algo tipo parent::nomeDoMetodo().