-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6abc7a7
commit 15a6ecc
Showing
2 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,349 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Polimorfismo" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Polimorfismo é um conceito de POO que permite que **diferentes classes** tenham **métodos com o mesmo nome**, mas que **funcionam de formas distintas**. Ou seja, o mesmo método pode ser implementado de maneiras diferentes em várias classes, dependendo do tipo de objeto que o invoca.\n", | ||
"\n", | ||
"É bem simples mesmo: classes diferentes que implementam métodos com mesmo nome que fazem coisas diferentes.\n", | ||
"\n", | ||
"Vamos construir juntos um exemplo prático para explicar este conceito." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Exemplo prático" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": { | ||
"vscode": { | ||
"languageId": "plaintext" | ||
} | ||
}, | ||
"source": [ | ||
"Pense em uma plataforma de e-commerce, quando você vai comprar algo. Existem várias formas de pagamento, certo? Pix, boleto, cartão de crédito. Vamos supor as seguintes regras:\n", | ||
"- Pix: desconto de 10%\n", | ||
"- Boleto: desconto de 5%\n", | ||
"- Cartão de crédito: taxa de juros de 2%\n", | ||
"\n", | ||
"São diferentes formas de se fazer a mesma coisa: processar pagamento. Este é o principal caso de uso de polimorfismo.\n", | ||
"\n", | ||
"Vamos desenvolver este exemplo passo a passo." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"class CartaoCredito:\n", | ||
" def processar_pagamento(self, valor: float) -> None:\n", | ||
" taxa = valor * 0.02 # 2% de juros\n", | ||
" total = valor + taxa\n", | ||
" print(\n", | ||
" f\"Pagamento de R${total:.2f} processado via cartão de crédito. (Juros: R${taxa:.2f})\"\n", | ||
" )" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Até aqui nenhuma novidade. Por simplicidade, eu sequer implementei o método construtor da classe com atributos. Vamos focar aqui em polimorfismo.\n", | ||
"\n", | ||
"O único ponto que vocês precisam notar é que eu criei um método `processar_pagamento`. Este é o método que será polimórfico.\n", | ||
"\n", | ||
"Na sequência, vamos criar outra classe para as outras formas de pagamamento." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"class CartaoCredito:\n", | ||
" def processar_pagamento(self, valor: float) -> None:\n", | ||
" taxa = valor * 0.02 # 2% de juros\n", | ||
" total = valor + taxa\n", | ||
" print(\n", | ||
" f\"Pagamento de R${total:.2f} processado via cartão de crédito. (Juros: R${taxa:.2f})\"\n", | ||
" )\n", | ||
"\n", | ||
"\n", | ||
"class Boleto:\n", | ||
" def processar_pagamento(self, valor: float) -> None:\n", | ||
" desconto = valor * 0.05 # 5% de desconto\n", | ||
" total = valor - desconto\n", | ||
" print(\n", | ||
" f\"Pagamento de R${total:.2f} processado via boleto bancário. (Desconto: R${desconto:.2f})\"\n", | ||
" )\n", | ||
"\n", | ||
"\n", | ||
"class Pix:\n", | ||
" def processar_pagamento(self, valor: float) -> None:\n", | ||
" desconto = valor * 0.10 # 10% de desconto\n", | ||
" total = valor - desconto\n", | ||
" print(\n", | ||
" f\"Pagamento de R${total:.2f} processado via pix. (Desconto: R${desconto:.2f})\"\n", | ||
" )" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Criamos mais 2 classes, `Boleto` e `Pix`, e ambas tem o método com o mesmo nome `processar_pagamento`. Mas, cada uma implementa de uma forma diferente, com suas regras específicas. Por fim, vamos criar um objeto de cada classe e chamar o método `processar_pagamento`." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 1, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Pagamento de R$1020.00 processado via cartão de crédito. (Juros: R$20.00)\n", | ||
"Pagamento de R$950.00 processado via boleto bancário. (Desconto: R$50.00)\n", | ||
"Pagamento de R$900.00 processado via pix. (Desconto: R$100.00)\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"class CartaoCredito:\n", | ||
" def processar_pagamento(self, valor: float) -> None:\n", | ||
" taxa = valor * 0.02 # 2% de juros\n", | ||
" total = valor + taxa\n", | ||
" print(\n", | ||
" f\"Pagamento de R${total:.2f} processado via cartão de crédito. (Juros: R${taxa:.2f})\"\n", | ||
" )\n", | ||
"\n", | ||
"\n", | ||
"class Boleto:\n", | ||
" def processar_pagamento(self, valor: float) -> None:\n", | ||
" desconto = valor * 0.05 # 5% de desconto\n", | ||
" total = valor - desconto\n", | ||
" print(\n", | ||
" f\"Pagamento de R${total:.2f} processado via boleto bancário. (Desconto: R${desconto:.2f})\"\n", | ||
" )\n", | ||
"\n", | ||
"\n", | ||
"class Pix:\n", | ||
" def processar_pagamento(self, valor: float) -> None:\n", | ||
" desconto = valor * 0.10 # 10% de desconto\n", | ||
" total = valor - desconto\n", | ||
" print(\n", | ||
" f\"Pagamento de R${total:.2f} processado via pix. (Desconto: R${desconto:.2f})\"\n", | ||
" )\n", | ||
"\n", | ||
"\n", | ||
"carta_credito = CartaoCredito()\n", | ||
"boleto = Boleto()\n", | ||
"pix = Pix()\n", | ||
"\n", | ||
"valor_tota_compra = 1_000\n", | ||
"\n", | ||
"carta_credito.processar_pagamento(valor_tota_compra)\n", | ||
"boleto.processar_pagamento(valor_tota_compra)\n", | ||
"pix.processar_pagamento(valor_tota_compra)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"O fato de eu ter chamado o mesmo método `processar_pagamento` para objetos de classes diferentes é o que caracteriza o polimorfismo. Dado que os objetos tem métodos com o mesmo nome, podemos começar a brincar com isso, por exemplo, colocando as formas de pagamento em uma lista e usar laço de repetição para averiguar todas as formas de pagamento." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 2, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Pagamento de R$1020.00 processado via cartão de crédito. (Juros: R$20.00)\n", | ||
"Pagamento de R$950.00 processado via boleto bancário. (Desconto: R$50.00)\n", | ||
"Pagamento de R$900.00 processado via pix. (Desconto: R$100.00)\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"# Ocultei a definição das classes acima CartaoCredito, Boleto e Pix para simplificar o código\n", | ||
"\n", | ||
"valor_tota_compra = 1_000\n", | ||
"\n", | ||
"forma_pagamentos = [CartaoCredito(), Boleto(), Pix()]\n", | ||
"\n", | ||
"for formato in forma_pagamentos:\n", | ||
" formato.processar_pagamento(valor_tota_compra)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Só é possível realizar esta operação porque usamos polimorfismo. Todas as classes tem métodos com o mesmo nome que fazem coisas diferentes." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Polimorfismo + herança" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Podemos unir o que aprendemos sobre herança com polimorfismo. Vou explicar de forma bem conceitual e simples com um exemplo didático." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 3, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Au! Au!\n", | ||
"Miau!\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"class Animal:\n", | ||
" def fazer_barulho(self):\n", | ||
" raise NotImplementedError(\n", | ||
" \"Subclasse precisa implementar o método `fazer_barulho`\"\n", | ||
" )\n", | ||
"\n", | ||
"\n", | ||
"class Cachorro(Animal):\n", | ||
" def fazer_barulho(self):\n", | ||
" return \"Au! Au!\"\n", | ||
"\n", | ||
"\n", | ||
"class Cat(Animal):\n", | ||
" def fazer_barulho(self):\n", | ||
" return \"Miau!\"\n", | ||
"\n", | ||
"\n", | ||
"animais = [Cachorro(), Cat()]\n", | ||
"\n", | ||
"for animal in animais:\n", | ||
" print(animal.fazer_barulho())" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"No código didático acima, temos uma superclasse `Animal` que tem um método `fazer_barulho()`. Porém, a superclasse não implementa este método, apenas define que ele existe. Cada subclasse de `Animal` implementa o método `fazer_barulho()` de uma forma diferente.\n", | ||
"\n", | ||
"O que acontece é que o método da superclasse `fazer_barulho()` é sobrescrito pelas subclasses. É uma forma de garantir que se os métodos não tiverem o mesmo nome, o código vai nos avisar que algo está errado." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Polimorfismo substituindo `if-else`" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Um dos benefícios do polimorfismo é que ele pode substituir `if-else`. Recuperando o exemplo do e-commerce, poderíamos fazer de outra forma, com `if-else`. Mas, o código ficaria menos flexível conforme ele for crescendo. Vejam como seria o código sem polimorfismo (e sem classes também), feito com `if-else`:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"forma_pagamento = \"pix\"\n", | ||
"\n", | ||
"valor_tota_compra = 1_000\n", | ||
"\n", | ||
"if forma_pagamento == \"cartao_credito\":\n", | ||
" taxa = valor_tota_compra * 0.02 # 2% de juros\n", | ||
" total = valor_tota_compra + taxa\n", | ||
"\n", | ||
"elif forma_pagamentos == \"boleto\":\n", | ||
" desconto = valor_tota_compra * 0.05\n", | ||
" total = valor_tota_compra - desconto\n", | ||
"\n", | ||
"elif forma_pagamentos == \"pix\":\n", | ||
" desconto = valor_tota_compra * 0.10\n", | ||
" total = valor_tota_compra - desconto\n", | ||
"\n", | ||
"print(f\"Pagamento de R${total:.2f} processado via {forma_pagamento}.\")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"```{admonition} Atenção (código menor nem sempre é melhor)\n", | ||
":class: attention\n", | ||
"Eu sei, você deve ter lido o código acima e pensado: \"Ah, mas o código ficou muito menor e mais simples\". Sim, é verdade. Mas, **código menor nem sempre é melhor**, coloque isso na sua cabeça! \n", | ||
"\n", | ||
"Aqui estamos trazendo um exemplo didático com 3 formas de pagamentos apenas. Imagine um sistema real com 10, 20, 30 classes e com várias outras regras que não apenas forma de pagamento. Isso que nem mostrei sobre `if-else` aninhados (um dentro do outro) aqui, que é um verdadeiro pesadelo para entender as regras de negócio. O código com `if-else` começaria a crescer de forma exponencial, cheio de regras espalhadas, o que acaba tornando-o difícil de manter e modificar no futuro.\n", | ||
"\n", | ||
"O polimorfismo é a forma mais elegante de simplificar o código (novamente, simples não é menor) e torná-lo mais flexível para futuras mudanças.\n", | ||
"```" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## ⚒️👷 (WIP) capítulo em construção ⚒️👷" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": ".venv", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.12.6" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 2 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters