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 2

    Exemplo Prático em PHP

    Vamos supor uma classe (primeira classe) com o nome ContaBanco, que pode ser criada por várias pessoas (portando, teria várias contas), com funções básicas como sacar, abrir e fechar contas, pagar mensalidades, etc. Ela teria um formato parecido com esses atributos e métodos:

    ContaBanco
    + numConta: int
    # tipo: String
    - dono: String
    - saldo: float
    - status: boolean
    + abrirConta(t: String): void
    + fecharConta(): void
    + depositar(v: float): void
    + sacar(v: float): void
    + pagarMensal(): void

    Crie um arquivo de classe PHP com o nome ContaBanco.php, e coloque esse código:

    
    class ContaBanco {
        public $numConta;
        protected $tipo;
        private $dono;
        private $saldo;
        private $status;
        
        public function abrirConta($t) {
            $this->setTipo($t);
            $this->setStatus(true);
            if($t == "CC") {
                $this->setSaldo(50);
            }
            else if($t == "CP") {
                $this->setSaldo(150);
            }
            echo "<p>Conta aberta com sucesso!</p>";
        }
        public function fecharConta() {
            if($this->getSaldo() > 0) {
                echo "<p>A conta ainda tem dinheiro, não podemos fechá-la!</p>";
            }
            else if($this->getSaldo() < 0) {
                echo "<p>A conta está em débito, não podemos fechá-la!</p>";
            }
            else {
                $this->setStatus(false);
                echo "<p>A conta de {$this->getDono()} foi encerrada com sucesso!</p>";
            }
        }
        public function depositar($v) {
            if($this->getStatus()) {
                $this->setSaldo($this->getSaldo() + $v);
                // $this->saldo = $this->saldo + $v;
                echo "<p>Depósito de R\${$v} autorizado na conta de {$this->getDono()}.</p>";
                // O \$ é pra não conflitar com o $ da variável.
            }
            else {
                echo "<p>Conta Fechada ou Inexistente!</p>";
            }
        }
        public function sacar($v) {
            if($this->getStatus()) {
                if($this->getSaldo() >= $v) {
                    $this->setSaldo($this->getSaldo() - $v);
                    // $this->saldo = $this->saldo - $v;
                    echo "<p>Saque de R\${$v} autorizado na conta de {$this->getDono()}.</p>";
                }
                else {
                    echo "<p>Saldo insuficiente para saque!</p>";
                }
            }
            else {
                echo "<p>Essa conta tá fechada ou não existe!</p>";
            }
        }
        public function pagarMensal() {
            if($this->getTipo() == "CC") {
                $v = 12;
            }
            else if($this->getTipo() == "CP") {
                $v = 20;
            }
            
            // Outra condição
            
            if($this->getStatus()) {
                $this->setSaldo($this->getSaldo() - $v);
                echo "<p>Mensalidade de R\${$v} debitada da conta de {$this->getDono()}.</p>";
            }
            else {
                echo "<p>Problemas na conta, não podemos cobrar.</p>";
            }
        }
        
        // Especiais:
        
        public function __construct() {
            $this->setSaldo(0);
            $this->setStatus(false);
            echo "<p>Conta criada com sucesso.</p>"; // Apague depois. Não é recomendado colocar echo aqui, é só pra testar.
        }
        
        public function __destruct() {
            echo "Objeto ContaBanco Destruído!<br/>";
        }
        
        public function getNumConta() {
            return $this->numConta;
        }
    
        public function getTipo() {
            return $this->tipo;
        }
    
        public function getDono() {
            return $this->dono;
        }
    
        public function getSaldo() {
            return $this->saldo;
        }
    
        public function getStatus() {
            return $this->status;
        }
        
        public function setNumConta($numConta) {
            $this->numConta = $numConta;
        }
    
        public function setTipo($tipo) {
            $this->tipo = $tipo;
        }
    
        public function setDono($dono) {
            $this->dono = $dono;
        }
    
        public function setSaldo($saldo) {
            $this->saldo = $saldo;
        }
    
        public function setStatus($status) {
            $this->status = $status;
        }
    }
    
    

    PS: Prefira usar métodos setter do que mexer direto nos atributos.

    Com esse código pronto, vá no index e coloque esses comandos pra criar os objetos:

    
    require_once "ContaBanco.php";
    
    $p1 = new ContaBanco();
    $p2 = new ContaBanco();
    
    $p1->abrirConta("CC");
    $p1->setNumConta(1111);
    $p1->setDono("Jubileu");
    
    $p2->abrirConta("CP");
    $p2->setNumConta(2222);
    $p2->setDono("Creuza");
    
    print_r($p1);
    print_r($p2);
    
    unset($p1);
    unset($p2);
    
    

    Para depositar, basta colocar isso, no final do código, antes do print_r():

    
    $p1->depositar(300);
    $p2->depositar(500);
    
    

    Para sacar, da mesma forma:

    
    $p2->sacar(100);
    
    

    E cobrar mensalidade também:

    
    $p1->pagarMensal();
    $p2->pagarMensal();
    
    

    Tente outros números, principalmente na parte de sacar. E tente as outras funções.

    Paralelamente, podemos fazer um exemplo apenas com métodos e atributos estáticos:

    
    class Ventilador {
        private static $ligado = false;
        private static $velocidade = 0;
        
        public static function ligar($vel) {
            self::$ligado = true;
    
            if($vel < 1) {
                $vel = 1;
            }
            else if($vel > 3) {
                $vel = 3;
            }
    
            self::$velocidade = $vel;
            
            printf("O ventilador está ligado, na velocidade de %d.<br/>", self::$velocidade);
        }
    
        public static function desligar() {
            self::$velocidade = 0;
            self::$ligado = false;
    
            echo "O ventilador está desligado!<br/>";
        }
    
        public static function getLigado() {
            return self::$ligado;
        }
    
        public static function getVelocidade() {
            return self::$velocidade;
        }
    
        public static function setLigado($ligado) {
            self::$ligado = $ligado;
        }
    
        public static function setVelocidade($velocidade) {
            self::$velocidade = $velocidade;
        }
    }
    
    

    E no programa principal:

    
    require_once "Ventilador.php";
    
    Ventilador::ligar(3);
    Ventilador::ligar(2);
    Ventilador::desligar();
    
    echo (Ventilador::getLigado() ? "true" : "false") . "<br/>";
    
    

    Encapsulamento

    Como sabemos, a POO tem três pilares, Encapsulamento, Herança e Polimorfismo (representados pelas letras EHP). O primeiro que vamos tratar é o encapsulamento.

    PS: Alguns consideram a Abstração um dos pilares também, o primeiro deles, mas na verdade ele está dentro do encapsulamento.

    Imagine que o carro, uma pilha ou qualquer outra coisa esteja numa "cápsula", no exemplo, vamos usar um controle remoto. O código estando encapsulado, ele está protegido internamente (como uma cápsula de remédio mesmo), deixando o que é pra ser privado "blindado" do usuário, e o que é público com contato permitido.

    A pilha também é um ótimo exemplo de encapsulamento, onde os componentes químicos que geram a eletricidade estão protegidos do contato humano (para proteger tanto os componentes quanto a pessoa), mas as partes metálicas estão disponíveis para fornecer energia ao produto. Além disso, a pilha sempre tem um formato padrão (tipo o AAA) para servir em vários produtos, independente da funcionalidade do produto. Mesmo se o componente interno da pilha for diferente (alcalina, comum, recarregável, etc.).

    No exemplo que usaremos, o controle remoto, a "cápsula" seria a capa que permite que nós interaja com ele, e o código seria os circuitos que operam isso, mas que não podemos tocar diretamente por segurança.

    O código "encapsulado" tem o mesmo padrão, e protege o código do usuário e vice-versa, impedindo que o programador cometa erros e prejudique o programa como um todo, independente do código encapsulado (ele só verificará as informações necessárias, sem precisar saber como ele funciona exatamente).

    Em outras palavras, encapsular é ocultar partes independentes da implementação, permitindo construir partes invisíveis ao mundo exterior.

    Basicamente, o uso de atributos privados e o acesso a eles ser feito através de métodos (como os getters e setters), já é o encapsulamento. Mas podemos fazer isso a nível de classe, usando uma interface, que obrigará a classe a ter todos os métodos descritos nesta interface e usar somente eles, além de proteger os atributos da classe implementadora do programa principal.

    Para criar um exemplo disso com um controle remoto, temos que definir uma interface (seria a "cápsula" do controle com os botões), de forma parecida com uma classe, mas sem atributos, e todos os métodos são públicos. E a classe ControleRemoto (como os circuitos encapsulados pela interface) terá os atributos e os métodos getters e setters privados (é o passo principal pra encapsular). Veja o que faremos no PHP:

    Encapsulamento

    Crie uma nova Interface em PHP com o nome Controlador.php com esse código:

    
    interface Controlador {
        public function ligar();
        public function desligar();
        public function abrirMenu();
        public function fecharMenu();
        public function maisVolume();
        public function menosVolume();
        public function ligarMudo();
        public function desligarMudo();
        public function play();
        public function pause();
    }
    
    

    PS: Os métodos abstratos significam que o método não será desenvolvido na interface, e sim na classe. É como um aviso pra interface que existe um método (por exemplo, de abrir, aumentar volume, etc.), mas a interface não precisa e nem sabe como o código funciona na classe, apenas esta que tem que implementar (executar). Se um método a ser implementado numa classe for estático, ele não pode ser indicado na interface. Interfaces agem de forma parecida com uma classe abstrata, só que sem implementações e sem atributos, apenas a assinatura dos métodos, que são abstratos por padrão, dispensando a indicação de abstract neles.

    Crie também a classe ControleRemoto.php, assim (os atributos deverão estar privados, os métodos getter e setter não são obrigatórios ser):

    
    require_once "Controlador.php";
    
    class ControleRemoto implements Controlador {
        private $volume;
        private $ligado;
        private $tocando;
        
        // Métodos especiais.
        
        public function __construct() {
            $this->volume = 50;
            $this->ligado = false;
            $this->tocando = false;
        }
    
        public function ligar() {
            $this->setLigado(true);
        }
        
        public function desligar() {
            $this->setLigado(false);
        }
        
        public function abrirMenu() {
            echo "<br/>Está ligado? " . ($this->getLigado() ? "SIM" : "NÃO");
            echo "<br/>Está tocando? " . ($this->getTocando() ? "SIM" : "NÃO");
            echo "<br/>Volume: " . $this->getVolume();
            for($i = 0; $i < $this->getVolume(); $i += 2) {
                echo "|";
            }
            echo "<br/>";
        }
        
        public function fecharMenu() {
            echo "<br/>Fechando menu...";
            
        }
    
        public function maisVolume() {
            if($this->getLigado()) {
                if($this->getVolume() < 100) {
                    $this->setVolume($this->getVolume() + 2);
                }
            }
            else {
                echo "<br/>ERRO! Está desligado, não posso aumentar o volume!";
            }
        }
    
        public function menosVolume() {
            if($this->getLigado()) {
                if($this->getVolume() > 0) {
                    $this->setVolume($this->getVolume() - 2);
                }
            }
            else {
                echo "<br/>ERRO! Está desligado, não posso diminuir o volume!";
            }
        }
        
        public function ligarMudo() {
            if($this->getLigado() && $this->getVolume() > 0) {
                $this->setVolume(0);
            }
        }
        
        public function desligarMudo() {
            if($this->getLigado() && $this->getVolume() == 0) {
                $this->setVolume(50);
            }
        }
    
        public function play() {
            if($this->getLigado() && !($this->getTocando())) {
                $this->setTocando(true);
            }
        }
        
        public function pause() {
            if($this->getLigado() && $this->getTocando()) {
                $this->setTocando(false);
            }
        }
        
        private function setVolume($volume) {
            $this->volume = $volume;
        }
        
        private function getVolume() {
            return $this->volume;
        }
    
        private function setLigado($ligado) {
            $this->ligado = $ligado;
        }
    
        private function getLigado() {
            return $this->ligado;
        }
        
        private function setTocando($tocando) {
            $this->tocando = $tocando;
        }
        
        private function getTocando() {
            return $this->tocando;
        }
    }
    
    

    Lembrando que, como poderemos usar apenas os métodos descritos na interface Controlador, os getters e setters de ControleRemoto não estariam acessíveis no programa principal, mesmo se estiverem públicos, isso garante o encapsulamento à nível de classe (que não faz muita diferença no PHP por ser uma linguagem dinamicamente tipada).

    PS: Usamos implements quando precisamos implementar uma interface, sendo obrigado executar todos os métodos descritos nela, é possível uma classe implementar mais de uma interface, com os nomes separados por vírgulas, usando algo como class NomeDaClasse implements NomeDaInterface1, NomeDaInterface2.

    E para criar o controle, fazemos assim no index:

    
    require_once "ControleRemoto.php";
    
    $c = new ControleRemoto();
    $c->ligar();
    $c->maisVolume(); // Aqui podemos colocar os outros métodos.
    $c->abrirMenu();
    
    

    Como exemplo, crie outros exemplos e funções baseados nesse controle.

    Essa é a vantagem do encapsulamento, ele protege o código contra ações externas e internas, e torna a forma interna de implementação invisível pro "lado de fora" (sabemos que quem mexe nela sabe como usar as funções, mas não como funcionam, até os métodos getter e setter são privados por isso).

    Em outras palavras, as interfaces estabelecem contratos entre as classes que as implementam, garantindo que elas tenham um certo comportamento.

    As interfaces também garantem o encapsulamento, já que podemos usar somente os métodos descritos na interface nos objetos, e não permitir a visualização de atributos da classe implementadora. Só que isso faz mais diferença em linguagens fortemente tipadas, como o Java, o que não é o caso do PHP.