Agora que você já viu na prática a importância da criação de métodos e argumentos, relembraremos, de maneira mais prática, alguns pilares da Programação Orientada a Objetos, como Herança e Polimorfismo, e praticaremos também o conceito de interface. Revisaremos os conceitos fundamentais de herança e polimorfismo na programação orientada a objetos, fornecendo uma base sólida para introduzir o conceito de interface em Java. Iniciaremos recapitulando conceitos de herança, destacando a capacidade de uma classe herdar atributos e comportamentos de outra classe, passando pela exploração do polimorfismo, que permite que objetos de diferentes classes sejam tratados de maneira uniforme, e finalizaremos com a explicação do conceito de interface como um contrato que define um conjunto de métodos que uma classe deve implementar.
Ao final desta lição, você terá uma compreensão aprimorada dos conceitos de herança e polimorfismo, além de adquirir conhecimentos fundamentais sobre o uso de interfaces em Java. Isso preparará o caminho para explorar tópicos mais avançados na programação orientada a objetos e promoverá o desenvolvimento de códigos eficientes e modularizados.
Pense que você esteja desenvolvendo um sistema complexo com várias classes interconectadas. Cada classe representa uma entidade específica com suas próprias características e comportamentos. No entanto surge um desafio: como garantir que essas classes possam interagir de maneira harmoniosa e flexível? Aqui entra o problema do acoplamento e da flexibilidade. Se as classes estiverem rigidamente acopladas umas às outras, qualquer alteração em uma delas pode causar impactos indesejados em todo o sistema. Além disso, como garantir que diferentes classes possam ser utilizadas de forma intercambiável em determinadas situações?
A solução para esse desafio é encontrada no conceito de interface na programação orientada a objetos. A interface serve como um contrato que define um conjunto de métodos que uma classe deve implementar. Isso proporciona uma camada de abstração, permitindo que diferentes classes implementem a mesma interface de maneira única. Portanto, o problema central aqui é a necessidade de garantir uma interação flexível e desacoplada entre classes em um sistema orientado a objetos complexos. A interface surge como uma solução elegante para esse problema, permitindo a criação de contratos comuns que promovem a padronização, a adaptabilidade e a manutenção simplificada do código.
No case fictício de hoje, você conhecerá a empresa TechSolutions, que enfrentava um desafio considerável em seu sistema de gestão empresarial, que abrange áreas, como finanças, recursos humanos e logística. O software estava se tornando difícil de manter devido à complexidade crescente e à falta de flexibilidade para incorporar novos módulos ou serviços. Por isso, a equipe de desenvolvimento decidiu abordar essa questão implementando interfaces em pontos-chave do sistema. Nesta lição, consideraremos o módulo de processamento de pagamentos como exemplo.
Antes da introdução de interfaces, cada método de pagamento (cartão de crédito, boleto, transferência bancária) era representado por uma classe separada, cada uma com sua própria lógica de processamento. Isso resultava em um acoplamento rígido, tornando difícil adicionar novos métodos de pagamento ou realizar alterações sem impactar o sistema como um todo. Com a introdução de uma interface chamada MétodoDePagamento, as classes de métodos de pagamento foram ajustadas para implementar essa interface. Agora, todas as classes, independentemente do método de pagamento, tinham que seguir o mesmo contrato definido pela interface.
Como benefício, a empresa viu, em seu código, o desacoplamento, a flexibilidade e a manutenção simplificada, o que nos demonstra como a implementação efetiva de interfaces pode proporcionar desacoplamento e flexibilidade ao sistema da TechSolutions, permitindo uma gestão mais eficiente e evolutiva de seu software empresarial.
Vamos aprender mais sobre esse assunto?
Na Programação Orientada a Objetos (POO), conceitos fundamentais, como herança, interface e polimorfismo, desempenham papéis cruciais na construção de sistemas robustos e flexíveis. De forma geral, eles são como pilares que sustentam a arquitetura de sistemas complexos, pois são responsáveis por permitirem a criação de sistemas modulares, flexíveis e de fácil manutenção. Dessa forma, dominar esses conceitos é essencial para o desenvolvimento de softwares de alta qualidade e, também, para a construção de sistemas escaláveis e eficientes.
Vamos compreender como cada um desses conceitos podem ser utilizados nos sistemas?
É um dos pilares fundamentais da programação orientada a objetos, que permite simplificar a complexidade de um sistema ao focar nos aspectos essenciais de um objeto, ignorando detalhes desnecessários. Por meio dela, você pode criar classes que representam conceitos genéricos, encapsulando atributos e comportamentos relevantes enquanto as complexidades internas são ocultadas. Isso facilita o desenvolvimento, a manutenção e o entendimento do código, promovendo uma abordagem mais modular e organizada na construção de sistemas complexos.
Apesar de você já ter visto o conceito de herança anteriormente nesta disciplina, realizaremos, agora, uma revisão mais prática desse conceito, que é fundamental em programação orientada a objetos. Conforme destacado por Sintes (2002), a herança permite que você baseie a definição de uma nova classe em uma classe previamente existente. Essa relação estabelece um vínculo “é um” entre as classes, por exemplo: “Carro” é um “Veículo”, em que “Veículo” é a classe denominada pai e “Carro” é uma classe que herda os atributos e métodos de “Veículo”, ou seja, uma subclasse.
A implementação de herança em um projeto orientado a objetos também ajuda bastante na reutilização de código, evitando, assim, a sua duplicação. Veja na Figura 1 um exemplo de implementação desse conceito:
Nesse exemplo que vimos, a classe “Animal” é a classe pai, também chamada de superclasse, porque ela será a base para as outras que implementam objetos do tipo “Animal”, e todo animal possui um atributo “nome” e um comportamento (método) “fazer barulho”. Já na Figura 2, temos um exemplo em que a classe é “Cachorro”, que é um animal e, portanto, está apto a herdar da classe “Animal”. Observe:
A princípio, para utilizar o atributo “nome”, assim como o construtor da classe “Animal”, a classe “Cachorro”, em seu método construtor, executa o construtor da classe pai para passagem do valor do atributo “nome”. Existe uma sobrescrita do método “fazerBarulho” que descreve mais especificamente o barulho/som que o cachorro faz. E ainda temos a implementação de um novo método específico da subclasse “Cachorro”, que é o “abanarRabo”.
De acordo com Sintes (2002), “polimorfismo” significa “muitas formas”. No contexto da programação, o polimorfismo permite que um único nome de classe ou nome de método represente um código diferente, selecionado por algum mecanismo automático. Essa característica proporciona flexibilidade e adaptabilidade no código. Na prática, existem dois tipos de implementação do conceito, um em tempo de compilação (sobrecarga de método) e o polimorfismo de tempo de execução (sobreposição de método). Na Figura 3, você poderá visualizar a implementação das duas formas na linguagem de programação Java.
Note que, na Figura 3, temos a classe “Figura”, que implementa um método com retorno genérico para cálculo de área (método “calcularArea()”) de uma figura geométrica. Agora, na Figura 4, temos a classe “Retangulo”, que herda da classe “Figura” o método “calcularArea()” e implementa a sobreposição do método retornando, então, o produto da largura vezes a altura. E logo na sequência implementa a sobrecarga de métodos incluindo um argumento fictício qualquer que poderia ser usado no cálculo de largura vezes altura.
A interface define um contrato que as classes devem seguir. De acordo com Versolatto (2023), a palavra que melhor define o que é uma interface na orientação a objetos é “contrato”, pois se trata de uma definição de comunicação que não pode ser quebrada, um protocolo de comunicação que define a forma como conversaremos. Diferentemente das classes, uma interface não contém implementação de métodos, ela apenas especifica os métodos que devem ser implementados. Além disso, ela permite a implementação de múltiplas interfaces em uma única classe e, com isso, resolve o problema da herança múltipla, proporcionando flexibilidade sem uma complexidade associada.
Observe a Figura 5, na qual temos a declaração da interface “Desenhavel” com uma única assinatura “desenhar();”.
Agora, na Figura 6, temos a declaração da interface “Redimensionavel” com uma única assinatura “redimensionar(double fator);”.
Na Figura 7, temos a implementação da classe “Retangulo”, que implementa as duas interfaces (Desenhavel e Redimensionavel), o que a obriga a implementar os métodos “desenhar” (linhas 11 a 13) e “redimensionar(double fator)” (linhas 16 a 20).
Sendo assim, os conceitos de herança, interface e polimorfismo são pilares fundamentais da Programação Orientada a Objetos, cada um desempenhando um papel único e essencial no desenvolvimento de sistemas robustos e flexíveis. Em conjunto, esses conceitos capacitam os desenvolvedores a criar sistemas modulares, extensíveis e de alta qualidade, capazes de se adaptar às necessidades em constante evolução do mundo da programação.
O conteúdo estudado nesta lição é essencial para solidificar sua compreensão dos fundamentos da Programação Orientada a Objetos (POO) e prepará-lo para desafios mais avançados. Ao revisitar os pilares da herança e polimorfismo, você fortalecerá sua capacidade de criar hierarquias de classes que compartilham características e comportamentos, promovendo a reutilização de código e a organização eficiente de sistemas complexos. Então, vamos mergulhar nesse conteúdo e consolidar seu conhecimento para alcançar novos horizontes na programação!
Agora chegou a hora de aplicar os conceitos de criação de herança, polimorfismo e interface em Java. Siga o passo a passo a seguir para a implementação:
Abra seu navegador web e acesse o site OnlineGDB em https://www.onlinegdb.com.
Escolha a linguagem de programação que deseja usar. Selecione “Java” na lista suspensa.
No editor de código, você pode escrever o código Java com implementação de funções e procedimentos conforme a Figura 8.
Observe que a classe “Veiculo” contém métodos básicos, como “acelerar” e “parar”, que exibem o texto “Veículo acelerando” e “Veículo parando”, respectivamente.
Depois, dessa implementação, crie uma classe derivada chamada “Carro” que herda da classe “Veiculo”. Sobrescreva o método “acelerar” para fornecer uma implementação específica para carros. Veja como isso deve ficar na Figura 9:
Observe que, ao invés de “Veiculo acelerando”, o método acelerar da classe “Carro” exibe “Carro acelerando”.
Agora, nessa próxima implementação, crie uma interface chamada “Motorizado” com um método “ligar” e faça com que a classe “Carro” o implemente. Veja como isso deve ficar na Figura 10:
Observe agora outro exemplo na Figura 11, em que a classe “Carro” implementa a interface “Motorizado”, e, por sua vez, ela se vê obrigada a implementar o método “ligar()”:
Agora que tal se desafiar ainda mais? Implemente uma classe “App” com o método “main()” para testar a utilização dos métodos da classe “Veiculo” e da classe “Carro” e confira os resultados. Discuta com seus colegas e professor sobre como adicionar implementações de sobrecarga de métodos e de outras interfaces.
SINTES, A. Aprenda programação orientada a objetos em 21 dias. São Paulo: Pearson, 2002.
VERSOLATTO, F. Sistemas orientados a objetos: conceitos e práticas. Rios de Janeiro: Freitas Bastos, 2023.