# Conceitos de Programação Orientada a Objetos (POO)
## Pilares da programação orientada a objetos
### 1) Abstração

* Conceito relacionado à capacidade de obter apenas características relevantes de um contexto (uma conta de banco, um produto, uma pessoa etc.) para possibilitar a criação de uma Classe.
* Na imagem acima, temos alguns casos de abstração, de modo que foram selecionados apenas aspectos importantes, naquele contexto, para compor determinada entidade.
### 2) Encapsulamento

* O encapsulamento é uma maneira de proteger o código, de modo que o acesso direto aos atributos de uma classe fica restringido. Também é possível criar restrições a determinados métodos.
* É possível ter acesso aos atributos de uma classe encapsulada por meio dos métodos ***getters*** e ***setters***.
* Não importa a forma utilizada para realizar um determinado procedimento, e sim se o resultado esperado de uma funcionalidade será entregue.
* Torna um software mais flexível, pois facilita a manutenção e a criação de novas utilidades.
---
### 3) Herança

* A classe que está sendo gerada (**subclasse**) herda todos os atributos e métodos da classe geradora (**superclasse**).
* Na imagem acima, quanto mais longe da classe Animal mais **especializada** é a classe e quanto mais próximo mais **genérica** é a classe.
* Nesse pilar da POO é importante saber as palavras-chave ***abstract*** e ***final***. Esses conceitos podem ser aplicados para classes e métodos e ganharão os seguintes comportamentos:
- **Classe abstrata**: não pode ser instanciada. Serve de "molde" para as subclasses.
- **Método abstrato**: declarado, mas não implementado. Basta uma classe ter um método abstrato para ela também ser abstrata. Além disso, caso uma subclasse herde um método abstrato, é obrigatório sobrescrever (conceito muito utilizado em Polimorfismo, outro pilar da POO) esse método com uma implementação.
- **Classe final**: não pode ser herdada.
- **Método final**: não pode ser sobrescrito por uma subclasse.
* Aqui também entra o termo ***protected***, um modificador de acesso que possibilita que apenas subclasses tenham acesso aos atributos de suas superclasses. Caso os atributos da superclasse fossem ***private***, nem as subclasses teriam acesso, sendo necessários os métodos *getters* e *setters*.
* Por fim, não é possível que uma subclasse *extends* duas superclasses, ou seja, não é permitido a herança múltipla na implementação clássica do Java. No entanto, é possível que uma classe esteja vinculada a mais de uma interface.
---
### 4) Polimorfismo

* **Polimorfismo de sobreposição (*Override*)**:
- Como citado no tópico método abstrato, caso uma subclasse estenda uma classe abstrata que possua métodos abstratos, é obrigatório que esses métodos abstratos sejam sobrescritos e implementados nessa subclasse.
- No caso da imagem acima, a classe abstrata *Funcionário* possui um método abstrato *calculoSalario()* e as especializações PessoaFisica e PessoaJuridica possuem seus próprios métodos de *calculoSalario()* (métodos com a mesma assinatura, mas com comportamentos distintos), ou seja, o método abstrato da superclasse é sobrescrito e implementado de diferentes formas nas subclasses.
- A associação (*upcasting*) do tipo específico (subclasse) com o tipo genérico (superclasse), por exemplo "Funcionario funcionario = *new* PessoaFisica("João", 35)", é feita em tempo de execução.
---
## Outros tópicos
### - Interface
* Interface é um tipo que define um conjunto de operações que uma determinada classe deve implementar.
* Na definição das interfaces há apenas a assinatura dos métodos, cabendo à classe que implementar essa interface definir o que for necessário.

### - Inversão de controle / injeção de dependência
* Inversão de controle: padrão de desenvolvimento que consiste em retirar da classe a responsabilidade de instanciar suas dependências.
* Injeção de dependência: maneira de realizar a inversão de controle. Um componente externo faz a instância da dependência (interface) e, após isso, é injetada no objeto "pai". Pode ser implementada por meio de um construtor (implementação da interface/dependência entra como argumento do construtor), por exemplo.



* No caso acima, a classe *BrazilTaxService* implementa a interface *TaxService* e a instanciação dessa dependência é realizada fora da classe *RentalService*. Além disso, a injeção de dependência ocorre por meio do construtor da classe *RentalService*.
* Dessa maneira, o código fica mais flexível, tendo em vista que a classe *RentalService* pode receber qualquer implementação da interface *TaxService*, como *GermanyTaxService*, *PortugalTaxService* etc.