Skip to content

Commit 6a9abb5

Browse files
authored
Merge pull request #313 from eduardoklosowski/artigo
Artigo: Encapsulamento da lógica do algoritmo
2 parents 10ae04b + 5838c6f commit 6a9abb5

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
Title: Encapsulamento da lógica do algoritmo
2+
Slug: encapsulamento-da-logica-do-algoritmo
3+
Date: 2021-03-02 15:00
4+
Category: Python
5+
Tags: python, poo
6+
Author: Eduardo Klosowski
7+
8+
Github: eduardoklosowski
9+
Twitter: eduklosowski
10+
Site: https://dev.to/eduardoklosowski
11+
About_author: Programador, formado em redes de computadores, estuda DevOps
12+
13+
Muitas listas de exercícios de lógica de programação pedem em algum momento que um valor seja lido do teclado, e caso esse valor seja inválido, deve-se avisar, e repetir a leitura até que um valor válido seja informado. Utilizando a ideia de [otimização do algoritmo passo a passo](https://dev.to/acaverna/otimizando-o-algoritmo-passo-a-passo-4co0), começando com uma solução simples, pretendo estudar como reduzir a duplicação de código alterando o algoritmo, encapsulando a lógica em funções, e encapsulando em classes.
14+
15+
## Exercício
16+
17+
Um exemplo de exercício que pede esse tipo de validação é a leitura de notas, que devem estar entre 0 e 10. A solução mais simples, consiste em ler um valor, e enquanto esse valor for inválido, dar o aviso e ler outro valor. Exemplo:
18+
19+
```python
20+
nota = float(input('Digite a nota: '))
21+
while nota < 0 or nota > 10:
22+
print('Nota inválida')
23+
nota = float(input('Digite a nota: '))
24+
```
25+
26+
Esse algoritmo funciona, porém existe uma duplicação no código que faz a leitura da nota (uma antes do *loop* e outra dentro). Caso seja necessário uma alteração, como a mudança da nota para um valor inteiro entre 0 e 100, deve-se alterar os dois lugares, e se feito em apenas um lugar, o algoritmo poderia processar valores inválidos.
27+
28+
### Alterando o algoritmo
29+
30+
Visando remover a repetição de código, é possível unificar a leitura do valor dentro do *loop*, uma vez que é necessário repetir essa instrução até que o valor válido seja obtido. Exemplo:
31+
32+
```python
33+
while True:
34+
nota = float(input('Digite a nota: '))
35+
if 0 <= nota <= 10:
36+
break
37+
print('Nota inválida!')
38+
```
39+
40+
Dessa forma, não existe mais a repetição de código. A condição de parada, que antes verificava se o valor era inválido (o que pode ter uma leitura não tão intuitiva), agora verifica se é um valor válido (que é geralmente é mais fácil de ler e escrever a condição). E a ordem dos comandos dentro do *loop*, que agora estão em uma ordem que facilita a leitura, visto que no algoritmo anterior era necessário tem em mente o que era executado antes do *loop*.
41+
42+
Porém esses algoritmos validam apenas o valor lido, apresentando erro caso seja informado um valor com formato inválido, como letras em vez de números. Isso pode ser resolvido tratando as exceções lançadas. Exemplo:
43+
44+
```python
45+
while True:
46+
try:
47+
nota = float(input('Digite a nota: '))
48+
if 0 <= nota <= 10:
49+
break
50+
except ValueError:
51+
...
52+
print('Nota inválida!')
53+
```
54+
55+
### Encapsulamento da lógica em função
56+
57+
Caso fosse necessário ler várias notas, com os algoritmos apresentados até então, seria necessário repetir todo esse trecho de código, ou utilizá-lo dentro de uma estrutura de repetição. Para facilitar sua reutilização, evitando a duplicação de código, é possível encapsular esse algoritmo dentro de uma função. Exemplo:
58+
59+
```python
60+
def nota_input(prompt):
61+
while True:
62+
try:
63+
nota = float(input(prompt))
64+
if 0 <= nota <= 10:
65+
break
66+
except ValueError:
67+
...
68+
print('Nota inválida!')
69+
return nota
70+
71+
72+
nota1 = nota_input('Digite a primeira nota: ')
73+
nota2 = nota_input('Digite a segunda nota: ')
74+
```
75+
76+
### Encapsulamento da lógica em classes
77+
78+
Em vez de encapsular essa lógica em uma função, é possível encapsulá-la em uma classe, o que permitiria separar cada etapa do algoritmo em métodos, assim como ter um método responsável por controlar qual etapa deveria ser chamada em qual momento. Exemplo:
79+
80+
```python
81+
class ValidaNotaInput:
82+
mensagem_valor_invalido = 'Nota inválida!'
83+
84+
def ler_entrada(self, prompt):
85+
return input(prompt)
86+
87+
def transformar_entrada(self, entrada):
88+
return float(entrada)
89+
90+
def validar_nota(self, nota):
91+
return 0 <= nota <= 10
92+
93+
def __call__(self, prompt):
94+
while True:
95+
try:
96+
nota = self.transformar_entrada(self.ler_entrada(prompt))
97+
if self.validar_nota(nota):
98+
break
99+
except ValueError:
100+
...
101+
print(self.mensagem_valor_invalido)
102+
return nota
103+
104+
105+
nota_input = ValidaNotaInput()
106+
107+
108+
nota = nota_input('Digite a nota: ')
109+
```
110+
111+
Vale observar que o método `__call__` permite que o objeto criado a partir dessa classe seja chamado como se fosse uma função. Nesse caso ele é o responsável por chamar cada etapa do algoritmo, como: `ler_entrada` que é responsável por ler o que foi digitado no teclado, `transformar_entrada` que é responsável por converter o texto lido para o tipo desejado (converter de `str` para `float`), e `validar_nota` que é responsável por dizer se o valor é válido ou não. Vale observar que ao dividir o algoritmo em métodos diferentes, seu código principal virou uma espécie de código comentado, descrevendo o que está sendo feito e onde está sendo feito.
112+
113+
Outra vantagem de encapsular a lógica em classe, em vez de uma função, é a possibilidade de generalizá-la. Se fosse necessário validar outro tipo de entrada, encapsulando em uma função, seria necessário criar outra função repetindo todo o algoritmo, alterando apenas a parte referente a transformação do valor lido, e validação, o que gera uma espécie de repetição de código. Ao encapsular em classes, é possível se aproveitar dos mecanismos de herança para evitar essa repetição. Exemplo:
114+
115+
```python
116+
class ValidaInput:
117+
mensagem_valor_invalido = 'Valor inválido!'
118+
119+
def ler_entrada(self, prompt):
120+
return input(prompt)
121+
122+
def transformar_entrada(self, entrada):
123+
raise NotImplementedError
124+
125+
def validar_valor(self, valor):
126+
raise NotImplementedError
127+
128+
def __call__(self, prompt):
129+
while True:
130+
try:
131+
valor = self.transformar_entrada(self.ler_entrada(prompt))
132+
if self.validar_valor(valor):
133+
break
134+
except ValueError:
135+
...
136+
print(self.mensagem_valor_invalido)
137+
return valor
138+
139+
140+
class ValidaNomeInput(ValidaInput):
141+
mensagem_valor_invalido = 'Nome inválido!'
142+
143+
def transformar_entrada(self, entrada):
144+
return entrada.strip().title()
145+
146+
def validar_valor(self, valor):
147+
return valor != ''
148+
149+
150+
class ValidaNotaInput(ValidaInput):
151+
mensagem_valor_invalido = 'Nota inválida!'
152+
153+
def transformar_entrada(self, entrada):
154+
return float(entrada)
155+
156+
def validar_valor(self, valor):
157+
return 0 <= valor <= 10
158+
159+
160+
nome_input = ValidaNomeInput()
161+
nota_input = ValidaNotaInput()
162+
163+
164+
nome = nome_input('Digite o nome: ')
165+
nota = nota_input('Digite a nota: ')
166+
```
167+
168+
Dessa forma, é possível reutilizar o código já existente para criar outras validações, sendo necessário implementar apenas como converter a `str` lida do teclado para o tipo desejado, e como esse valor deve ser validado. Não é necessário entender e repetir a lógica de ler o valor, validá-lo, imprimir a mensagem de erro, e repetir até que seja informado um valor válido.
169+
170+
## Considerações
171+
172+
É possível encapsular a lógica de um algoritmo em funções ou em classes. Embora para fazê-lo em uma classe exija conhecimentos de programação orientada a objetos, o seu reaproveitamento é facilitado, abstraindo toda a complexidade do algoritmo, que pode ser disponibilizado através de uma biblioteca, exigindo apenas a implementações de métodos simples por quem for a utilizar.
173+
174+
Ainda poderia ser discutido outras formas de fazer essa implementação, como passar funções como parâmetro e a utilização de [corrotinas](https://docs.python.org/pt-br/3/library/asyncio-task.html) no encapsulamento do algoritmo em função, assim como a utilização de [classmethod](https://docs.python.org/pt-br/3/library/functions.html#classmethod), [staticmethod](https://docs.python.org/pt-br/3/library/functions.html#staticmethod) e [ABC](https://docs.python.org/pt-br/3/library/abc.html) no encapsulamento do algoritmo em classes.
175+
176+
---
177+
178+
Esse artigo foi publicado originalmente no [meu blog](https://eduardoklosowski.github.io/blog/), passe por lá, ou siga-me no [DEV](https://dev.to/eduardoklosowski) para ver mais artigos que eu escrevi.

0 commit comments

Comments
 (0)