Orientação a objetos - Conceitos essenciais [exemplos em PHP] Artigo

Conheça os cursos gratuitos do WebDevBr! - Inscreva-se!


Este artigo foi publicado a 9 meses, 3 semanas, 2 dias atrás.

Orientação a objetos é essencial para qualquer programador PHP (e de qualquer outra linguagem) que leve sua vida profissional a sério, mas muito do que abrange OO precisa de teoria, o que é horrível na realidade do ensino de programação nos dias de hoje, o público quer prática e mais prática e nem sempre isso é possível. Acredite em mim, as vezes a prática atrapalha um pouco.

Este artigo terá sim exemplos, mas é essencialmente teórico e será pouco lido, infelizmente. Fico feliz se conseguir alguma qualidade em vez de quantidade aqui.

Classe != de Objeto

Sim, classe e objeto não são a mesma coisa, uma classe define o comportamento que o (ou os) objeto terá e quais estados poderá manter.

Gostou deste artigo?

Receba atualizações semanais com novos artigos do WebDevBr e outras dicas!

Completando a sentença acima, comportamento é igual a métodos e estado são os atributos, em um exemplo prático:

    <?php

    namespace Este\É\O\Namespace\Da;

    class Classe
    {
            public $esteÉOAtributoOuEstadoAManter;

            public function esteÉOMétodoOuComportamento()
            {
            }
    }

Claro, usei acentos e não deveria, mas foi apenas para facilitar o entendimento, eles vão gerar um erro.

Se você não conhece namespaces, eles servem para complementar o nome da classe, assim da pra informar que determinada classe pertence a algum pacote e que foi escrita por determinada pessoa ou empresa, por exemplo:

Symfony\Core\Request -> Classe Request do pacote (ou biblioteca, ou library) do Synfony. Zend\Acl -> Classe Acl (de controle de acesso de usuários) da Zend (Empresa ou Framework) WebDevBr\OAuth2\Server -> Classe Server do pacote OAuth2 do WebDevBr WebDevBr\Html\Tags\Img -> Classe Img, dentro do diretório Tags do Pacote Html do WebDevBr

Os objetos ou instâncias de classe são o resultado da classe ao ser usada, existem várias formas de fazer isso no PHP, mas a mais usada é com a palavra-chave new:

    use Este\É\O\Namespace\Da\Classe;

    $objeto = new Classe;
    $objeto->esteÉOMétodoOuComportamento();

A palavra-chave use serve para informar que aquele arquivo irá utilizar aquela classe, isso ajuda a organizar, já que teremos uma lista do que usamos logo de cara. Outra vantagem é que poderemos usar apenas o nome da classe (sem o namespace) e até dar apelidos usando o as ("como" em inglês):

    use Este\É\O\Namespace\Da\Classe as C;

    $objeto = new C;
    $objeto->esteÉOMétodoOuComportamento();

Ao contrário do que muitos pensam, não é necessário terminar a instanciação com parenteses () e da pra instânciar e usar métodos ou atributos em uma única linha.

    use Este\É\O\Namespace\Da\Classe;

    $retornoDoMetodo = (new Classe)->esteÉOMétodoOuComportamento();

Quando colocamos new Classe dentro de parenteses podemos chamar um método ou atributo na sequência, ou seja, ele instancia e já retorna a classe, especialmente útil quando precisamos executar um método uma única vez sem precisar criar mais uma variável na memória.

Um bom exemplo de Classe versus Objeto seria uma classe Carros que definiria vários objetos: Gol, HB20, Uno Vivace. Que tal você montar este exemplo? Consegue?

Os pilares da orientação a objetos

A orientação a objetos é definida em 4 pilares básicos, cada qual respondendo a um dos aspectos da orientação a objetos.

Herança

Herança ou generalização seria a capacidade de uma classe herdar atributos e métodos de outra classe, neste contexto a classe herdada se chama super-classe e a que herda sub-classe, por exemplo, a classe Carros é uma sub-classe da super-classe Veiculos.

No PHP herança se da de duas formas:

Usando a (mais conhecida) palavra-chave extends, a famosa herança vertical.

Com use dentro da classe, que neste contexto vai funcionar do que foi apresentei antes sobre o use.

Por exemplo:

<?php

trait Body
{
        public function paragrafo($texto)
        {
            return sprintf('<p>%s</p>', $texto);
        }
}

trait Head
{
        public function title($texto)
        {
            return sprintf('<title>%s</title>', $texto);
        }
}

class Html
{
        use Body;
        use Head;

        public function head($texto)
        {
                $this->head = $texto;
        }

        public function body($texto)
        {
                $this->body = $texto;
        }
}

class Html5 extends Html
{
        public function renderizar()
        {
                $html = trim('<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    '.$this->head.'
</head>
<body>
    '.$this->body.'
</body>
</html>
                    ');

                return $html;
        }
}

$html5 = new Html5;
$html5->head($html5->title('Título da página'));
$html5->body($html5->paragrafo('Hello World!'));
echo $html5->renderizar();

Embora o exemplo seja simples, a ideia por traz disto é a reusabilidade e capacidade de espanção da coisa toda (alguns dos principais motivos pelos quais usamos Orientação a Objetos), Eu tenho a sub-classe Html5 (que herda da super-classe Html), ela apenas renderiza o código com o "layout" que passei (neste caso, uma estrutura básica HTML 5), tudo é feito por intermédio das classes que foram herdadas, se eu quiser dar suporte a XHTML preciso apenas criar esta classe e herdar Html (minha super-classe).

Note que agora o use não apenas informa a classe que vamos usar, ele carrega a classe dentro da outra, essa é a herança horizontal. O extends é a herança vertical

Herança horizontal quer dizer que podemos carregar várias classes enquanto que a herança vertical carrega apenas a anterior (e anterior a esta, infinitamente). Quando é feita a herança vertical a sub-classe passa a "ser do tipo" da super-classe, isso quer dizer que para efeitos de tipagem e comparação, Html 5 é do tipo Html:

    <?php

    class Navegador
    {
        public function exibirNaTela(Html $html)
        {
            echo $html->renderizar();
        }
    }

Viu como eu informei o nome da super-classe Html ali no método da classe Navegador, quer dizer que o objeto só vai aceitar a classe Html ou as que a herdarem verticalmente (com extends), legal né.

Encapsulamento

Encapsulamento é meio natural de acontecer, mas você via ter que dar uma forcinha as vezes, ele define quem e o que pode ver determinado atributo ou método. O PHP traz três palavras-chave para ajudar:

  • private: Informa que apenas o objeto, dono do atributo/método pode ter acesso ao recurso
  • protected: Informa que apenas o objeto e classes que fazem parte da herança (tanto subs quanto supers) podem ter acesso ao recurso
  • public: Informa que pode ser acessado interna e externamente (este é o padrão)

Lembrando que, de acordo com a PSR-2, você deve informar a visibilidade de todo e qualquer atributo/método, mesmo que seja public, embora omitir não gere erros vai ajudar na organização do código.

Só pra reforçar, omitir a visibilidade torna o atributo/método público.

Em ORMs como Eloquent e CakeORM, todos os atributos da entidade são protected e acessados via métodos mágicos ( __call() e __get()).

Abstração

Outro ponto importante de uma classe é que ela pode definir outras classes, ou seja, focar nos aspectos essenciais (que precisam existir) e ignorar os acidentais.

Uma classe abstrata pode ser definida usando a palavra-chave abstract, e ela pode carregar métodos e atributos comuns a outras classes ou forçar a criação deles, um bom exemplo seria nossa classe Html do exemplo anterior.

abstract class Html
{
        use Body;
        use Head;

        public function renderizar();

        public function head($texto)
        {
                $this->head = $texto;
        }

        public function body($texto)
        {
                $this->body = $texto;
        }
}

Notou que eu tenho um método renderizar() sem um "corpo", ou seja, ele não tem chaves de abertura e fechamento { }. Isso quer dizer que toda classe que herdar Html deve criar um método renderizar() ou um erro vai acontecer, em outras palavras, a classe Html garante que o método exista sempre e tenha o mesmo nome, visibilidade e parâmetros, agora o código abaixo faz muito mais sentido, não?

    <?php

    class Navegador
    {
        public function exibirNaTela(Html $html)
        {
            echo $html->renderizar();
        }
    }

Quero dizer, eu informando que a variável Html tem uma classe do tipo Html e que implementa obrigatóriamente um método renderizar() eu garanto que o código em Navegador vai funciona.

Outra forma de forçar classes a criar métodos é com interfaces, a vantagem é que uma classe pode ter várias interfaces, ou seja, ter vários tipos. Uma interface é criada usando a palavra-chave interface e carregada com implements:

    <?php
    interface a
    {
            public function foo();
    }

    interface b extends a
    {
            public function baz(Baz $baz);
    }

    class c implements b
    {
            public function foo()
            {
            }

            public function baz(Baz $baz)
            {
            }
    }

Viu? Interfaces podem herdar outras interfaces.

Polimorfismo

Não sei porque, toda vez que converso sobre polimorfismo com algum dev uma discussão (a maioria muito proveitosa) se segue.

Polimorfismo é a capacidade de uma classe implementar métodos de mesma assinatura, mas com comportamentos distintos. Lembra de quando falei sobre interfaces e classes abstratas forçarem a classe implementar aquele método em questão? É exatamente isso! Resultados diferentes para classes que pedem métodos com a mesma "estrutura".

Conclusão

Este artigo tem como foco mostrar para novos programadores os caminhos da orientação a objetos e ditar o que é o que para quem "já sabe o que é na prática".

Espero que seja útil e tire você do lado negro.


Cursos relacionados


* Parcelamento apenas cartão de crédito! Pode haver uma pequena variação no parcelamento em relação a simulações apresentadas!