Guia fundamental da programação orientada a objetos - Final
Este artigo é parte de uma série.
Agora que nós já vimos os pilares da POO e a sua importância, está na hora de abordar mais alguns aspectos importantes de orientação a objetos neste último artigo. Vamos falar um pouco sobre classes abstratas, interfaces e classes finais (conhecida como classe selada em algumas linguagens).
O que são classes abstratas?
Elas são classes que não podem ser instanciadas. Ou seja, você não pode criar objetos a partir de uma classe abstrata. Aí você pode pensar: “Mas pra que serve uma classe que não pode virar objeto?”. Bom, você ainda pode fazer herança com uma classe abstrata, e é aí que vem a grande utilidade delas. Imaginemos que eu queira implementar algumas classes de Pagamento. Uma pra pagamento via cartão de crédito, outra para pagamento com boleto, e por fim uma para pagamento via pix.
Muitas coisas nessas três formas de pagamento são bem parecidas, não? Sempre existe uma pessoa que paga, um horário em que o pagamento e também o seu valor. Essas informações podem, e devem ser abstraídas, não faz o menor sentido ficar repetindo coisas atoa.
E é aí que nós podemos criar uma classe PagamentoBase que não vai ser capaz de realizar um pagamento, mas terá a responsabilidade de guardar essas partes comuns a todos os tipos de pagamento.
E não faz o menor sentido criar uma instância de PagamentoBase, já que ele não é uma classe responsável por fazer o pagamento em si, mas para abstrair ao máximo coisas para as outras classes usarem. Então nesse caso, nós podemos marcar essa classe como sendo abstrata, então será impossível de criar um objeto a partir de PagamentoBase.
Claro, classes abstratas também podem ter outras responsabilidades dependendo de como você as implementa, mas na minha visão, a principal função delas é abstrair coisas para que outras classes “normais” consigam usar e se beneficiar, aumentando a reusabilidade do código.
Além disso, lembre-se que também é possível sobrescrever os métodos da nossa classe abstrata, então, por exemplo, eu posso sobrescrever o método de notificar o usuário quando o pagamento é feito com sucesso.
Abaixo implemento o exemplo citado nos parágrafos anteriores.
|
|
O que são interfaces?
A primeira coisa que você precisa ter em mente é que interface NÃO são classes, logo é impossível instanciar objetos a partir da interface. Mas você pode pensar, “se eu não consigo criar objetos a partir de interfaces, como que eu vou usar elas no meu código?”, e eu vou te responder que interfaces são usadas como se fossem ”contratos”.
Quando você assina um contrato, você é obrigado pela lei a cumprir o que está escrito no contrato. Ou seja, o contrato é uma forma de garantir que tu cumpra o que prometeu cumprir. Interfaces funcionam de forma parecida.
Digamos que eu tenha uma classe Carro, e uma classe Moto, e você concorda comigo que todo veículo precisa andar para ser útil, não é mesmo? Então, nessa situação, se essas classes não tiverem um método andar(), elas serão inúteis pra mim, pois um veículo que não anda é inútil.
E é justamente nesse cenário que as interfaces aparecem pra salvar o dia, eu posso criar uma interface chamada “ContratoVeiculo” e lá colocar o método andar(). Logo após isso eu faço a minha classe implementar essa interface. Isso vai fazer com que essa classe seja obrigada a implementar os métodos definidos na interface.
A interface não tem a obrigação de dizer como um método deve funcionar (e nem pode fazer isso), por isso em interfaces nós não implementamos o método, apenas escrevemos a assinatura do método, ou seja, escrevemos o seu nome, quais parâmetros ele recebe, e qual o tipo do retorno desse método.
É obrigação da classe que implementa esse interface dizer como vai ser toda a lógica do meu método, mas esse método precisa respeitar a assinatura da interface, então ele precisa receber os mesmos tipos de parâmetros e retornar o tipo de valor definido na interface.
A vantagem em ter essa abordagem (interfaces serem proibidas de ditar o funcionamento dos métodos) é que eu vou poder usar a mesma interface para classes diferentes, que terão lógicas de funcionamento diferentes. Como você já deve saber, carros e motos andam de formas diferentes, mas como a minha interface ContratoVeiculo é “agnóstica”, eu consigo usar ela tanto na classe Carro quanto na classe Moto.
Abaixo eu mostro um trecho de código PHP que implementa essas classes e interface citados acima.
|
|
E o que são classes seladas?
E por fim, vamos falar um pouco sobre as classes finais, ou classes seladas, o nome varia de acordo com a linguagem de programação que você estiver usando (em PHP e Java é classe final e no C# é classe selada). Lembra que as classes abstratas não podem ser instanciadas, mas podiam ser herdadas? Com as classes finais acontece o oposto, elas podem ser instanciadas, mas não podem ser herdadas.
A razão para se usar uma classe desse tipo é que a herança também tem o seu ônus, e pode ser que em algum caso especifico você quer evitar que algum outro programador extenda a sua classe. Deixo abaixo a opinião de um usuário do stackoverflow sobre o assunto.
“É uma forma de dizer que esta classe não pode ser herdada, o que costuma ser uma boa ideia. Desenvolver classes preparadas para serem herdadas é muito mais difícil, a manutenção seguinte se torna um fardo. Acho até que o padrão deveria ser a classe não herdável.”
Abaixo deixo um exemplo de classe final no PHP.
|
|
Conclusão
Bom, com esse artigo nós cobrimos todas as partes que eu considero fundamentais para que você comece a usar a orientação a objetos. Existem muitas coisas que não foram cobertas nessa série e que estão relacionadas com POO, como por exemplo funções amigas e generics. Mas falar disso fugiria um pouco da finalidade dessa série, que é cobrir os fundamentos. Vejo vocês em próximos artigos! :)