ProceduralPoo
Comparando Aplicativos: Procedural vs. Orientado a Objetos¶
Entendendo os Paradigmas
Antes de começarmos a criar os aplicativos, vamos entender brevemente a diferença entre programação procedural e orientada a objetos:
- Programação Procedural: Foca em uma sequência de passos para realizar uma tarefa. O código é organizado em funções que realizam ações específicas.
- Programação Orientada a Objetos: Organiza o código em objetos que representam entidades do mundo real. Cada objeto possui atributos (características) e métodos (ações).
Exemplo: Cálculo de Área de um Retângulo
Vamos criar um aplicativo simples que calcula a área de um retângulo usando ambos os paradigmas.
### 1. Programação Procedural
def calcular_area(base, altura):
"""Calcula a área de um retângulo.
Args:
base (float): A base do retângulo.
altura (float): A altura do retângulo.
Returns:
float: A área do retângulo.
"""
area = base * altura
return area
# Obtendo os dados do usuário
base = float(input("Digite a base do retângulo: "))
altura = float(input("Digite a altura do retângulo: "))
# Calculando a área
resultado = calcular_area(base, altura)
# Imprimindo o resultado
print("A área do retângulo é:", resultado)
--------------------------------------------------------------------------- StdinNotImplementedError Traceback (most recent call last) Cell In[1], line 18 15 return area 17 # Obtendo os dados do usuário ---> 18 base = float(input("Digite a base do retângulo: ")) 19 altura = float(input("Digite a altura do retângulo: ")) 21 # Calculando a área File c:\Users\00661711722\Documents\PBE_24.2_8002\.venv\Lib\site-packages\ipykernel\kernelbase.py:1281, in Kernel.raw_input(self, prompt) 1279 if not self._allow_stdin: 1280 msg = "raw_input was called, but this frontend does not support input requests." -> 1281 raise StdinNotImplementedError(msg) 1282 return self._input_request( 1283 str(prompt), 1284 self._parent_ident["shell"], 1285 self.get_parent("shell"), 1286 password=False, 1287 ) StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
### 2. Programação Orientada a Objetos
class Retangulo:
def __init__(self, base, altura):
self.base = base
self.altura = altura
def calcular_area(self):
return self.base * self.altura
# Obtendo os dados do usuário
base = float(input("Digite a base do retângulo: "))
altura = float(input("Digite a altura do retângulo: "))
# Criando um objeto Retangulo
retangulo = Retangulo(base, altura)
# Calculando a área
resultado = retangulo.calcular_area()
# Imprimindo o resultado
print("A área do retângulo é:", resultado)
--------------------------------------------------------------------------- StdinNotImplementedError Traceback (most recent call last) Cell In[2], line 12 9 return self.base * self.altura 11 # Obtendo os dados do usuário ---> 12 base = float(input("Digite a base do retângulo: ")) 13 altura = float(input("Digite a altura do retângulo: ")) 15 # Criando um objeto Retangulo File c:\Users\00661711722\Documents\PBE_24.2_8002\.venv\Lib\site-packages\ipykernel\kernelbase.py:1281, in Kernel.raw_input(self, prompt) 1279 if not self._allow_stdin: 1280 msg = "raw_input was called, but this frontend does not support input requests." -> 1281 raise StdinNotImplementedError(msg) 1282 return self._input_request( 1283 str(prompt), 1284 self._parent_ident["shell"], 1285 self.get_parent("shell"), 1286 password=False, 1287 ) StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
Analisando os Exemplos
- Procedural: O código é organizado em uma função
calcular_area
que realiza o cálculo. Os dados são passados como argumentos para a função. - Orientado a Objetos: Criamos uma classe
Retangulo
que representa um retângulo. A classe possui atributosbase
ealtura
e um métodocalcular_area
para calcular a área.
Quando usar cada paradigma?
- Procedural: Ideal para tarefas simples e diretas, onde a organização do código em funções é suficiente.
- Orientado a Objetos: Mais adequado para sistemas complexos, onde a organização do código em objetos facilita a manutenção e reutilização.
Outras Considerações
- Encapsulamento: Na POO, os atributos e métodos podem ser públicos ou privados, controlando o acesso aos dados.
- Herança: Classes podem herdar atributos e métodos de outras classes, promovendo a reutilização de código.
- Polimorfismo: Objetos de diferentes classes podem responder de forma diferente ao mesmo método.
Aplicativos Mais Complexos
Para aplicativos mais complexos, como jogos, interfaces gráficas ou sistemas de gerenciamento de dados, a POO geralmente é a abordagem preferida devido à sua capacidade de modelar o mundo real de forma mais natural.
Exercícios
- Expanda o exemplo: Crie uma classe
Circulo
com atributosraio
e um método para calcular a área. - Implemente um jogo simples: Crie um jogo de adivinhação usando classes para representar o jogador, o computador e o jogo em si.
# Crie uma interface gráfica:** Utilize uma biblioteca como Kyvi para criar uma interface gráfica para o cálculo da área do retângulo.
! pip install "kivy[base]" kivy_examples
Requirement already satisfied: kivy_examples in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (2.3.0) Requirement already satisfied: kivy[base] in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (2.3.0) Requirement already satisfied: Kivy-Garden>=0.1.4 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from kivy[base]) (0.1.5) Requirement already satisfied: docutils in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from kivy[base]) (0.21.2) Requirement already satisfied: pygments in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from kivy[base]) (2.18.0) Requirement already satisfied: kivy-deps.angle~=0.4.0 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from kivy[base]) (0.4.0) Requirement already satisfied: kivy-deps.sdl2~=0.7.0 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from kivy[base]) (0.7.0) Requirement already satisfied: kivy-deps.glew~=0.3.1 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from kivy[base]) (0.3.1) Requirement already satisfied: pypiwin32 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from kivy[base]) (223) Requirement already satisfied: pillow<11,>=9.5.0 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from kivy[base]) (10.4.0) Requirement already satisfied: requests in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from kivy[base]) (2.32.2) Requirement already satisfied: pywin32>=223 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from pypiwin32->kivy[base]) (306) Requirement already satisfied: charset-normalizer<4,>=2 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from requests->kivy[base]) (3.3.2) Requirement already satisfied: idna<4,>=2.5 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from requests->kivy[base]) (3.7) Requirement already satisfied: urllib3<3,>=1.21.1 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from requests->kivy[base]) (2.2.1) Requirement already satisfied: certifi>=2017.4.17 in c:\users\00661711722\documents\pbe_24.2_8002\.venv\lib\site-packages (from requests->kivy[base]) (2024.2.2)
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
class Retangulo:
def __init__(self, base, altura):
self.base = base
self.altura = altura
def calcular_area(self):
return self.base * self.altura
class CalculadoraAreaApp(App):
def build(self):
layout = BoxLayout(orientation='vertical')
self.base_input = TextInput(multiline=False)
self.altura_input = TextInput(multiline=False)
self.resultado_label = Label(text='Resultado:')
calcular_button = Button(text='Calcular')
calcular_button.bind(on_press=self.calcular_area)
layout.add_widget(self.base_input)
layout.add_widget(self.altura_input)
layout.add_widget(calcular_button)
layout.add_widget(self.resultado_label)
return layout
def calcular_area(self, instance):
try:
base = float(self.base_input.text)
altura = float(self.altura_input.text)
retangulo = Retangulo(base, altura)
resultado = retangulo.calcular_area()
self.resultado_label.text = f'A área é: {resultado}'
except ValueError:
self.resultado_label.text = 'Digite valores numéricos.'
if __name__ == '__main__':
CalculadoraAreaApp().run()
[INFO ] [Logger ] Record log in C:\Users\00661711722\.kivy\logs\kivy_24-11-08_3.txt
[INFO ] [deps ] Successfully imported "kivy_deps.angle" 0.4.0
[INFO ] [deps ] Successfully imported "kivy_deps.glew" 0.3.1
[INFO ] [deps ] Successfully imported "kivy_deps.sdl2" 0.7.0
[INFO ] [Kivy ] v2.3.0
[INFO ] [Kivy ] Installed at "c:\Users\00661711722\Documents\PBE_24.2_8002\.venv\Lib\site-packages\kivy\__init__.py"
[INFO ] [Python ] v3.12.7 (tags/v3.12.7:0b05ead, Oct 1 2024, 03:06:41) [MSC v.1941 64 bit (AMD64)]
[INFO ] [Python ] Interpreter at "c:\Users\00661711722\Documents\PBE_24.2_8002\.venv\Scripts\python.exe"
[INFO ] [Logger ] Purge log fired. Processing...
[INFO ] [Logger ] Purge finished!
[INFO ] [Factory ] 195 symbols loaded
[INFO ] [Image ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
[INFO ] [Text ] Provider: sdl2
[INFO ] [Window ] Provider: sdl2
[INFO ] [GL ] Using the "OpenGL" graphics system
[INFO ] [GL ] GLEW initialization succeeded
[INFO ] [GL ] Backend used <glew>
[INFO ] [GL ] OpenGL version <b'4.6.0 NVIDIA 556.12'>
[INFO ] [GL ] OpenGL vendor <b'NVIDIA Corporation'>
[INFO ] [GL ] OpenGL renderer <b'NVIDIA T1000/PCIe/SSE2'>
[INFO ] [GL ] OpenGL parsed version: 4, 6
[INFO ] [GL ] Shading version <b'4.60 NVIDIA'>
[INFO ] [GL ] Texture max size <32768>
[INFO ] [GL ] Texture max units <32>
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
---------------------------------------------------- OSError Traceback (most recent call last) Cell In[4], line 40 37 self.resultado_label.text = 'Digite valores numéricos.' 39 if __name__ == '__main__': ---> 40 CalculadoraAreaApp().run() File c:\Users\00661711722\Documents\PBE_24.2_8002\.venv\Lib\site-packages\kivy\app.py:955, in App.run(self) 952 def run(self): 953 '''Launches the app in standalone mode. 954 ''' --> 955 self._run_prepare() 956 runTouchApp() 957 self._stop() File c:\Users\00661711722\Documents\PBE_24.2_8002\.venv\Lib\site-packages\kivy\app.py:924, in App._run_prepare(self) 922 if not self.built: 923 self.load_config() --> 924 self.load_kv(filename=self.kv_file) 925 root = self.build() 926 if root: File c:\Users\00661711722\Documents\PBE_24.2_8002\.venv\Lib\site-packages\kivy\app.py:677, in App.load_kv(self, filename) 675 else: 676 try: --> 677 default_kv_directory = dirname(getfile(self.__class__)) 678 if default_kv_directory == '': 679 default_kv_directory = '.' File C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.2032.0_x64__qbz5n2kfra8p0\Lib\inspect.py:923, in getfile(object) 921 return module.__file__ 922 if object.__module__ == '__main__': --> 923 raise OSError('source code not available') 924 raise TypeError('{!r} is a built-in class'.format(object)) 925 if ismethod(object): OSError: source code not available
Vamos criar um exemplo mais complexo: Simulando um jogo de RPG simples¶
Contexto:
Imagine que queremos criar um jogo de RPG básico, onde o jogador possui um personagem com atributos como nome, classe, pontos de vida e mana. O personagem pode atacar, defender e usar habilidades.
### 1. Programação Procedural
def criar_personagem():
nome = input("Digite o nome do seu personagem: ")
classe = input("Digite a classe do seu personagem: ")
vida = 100
mana = 50
return nome, classe, vida, mana
def atacar(vida_inimigo):
dano = 10
vida_inimigo -= dano
print("Você causou 10 de dano ao inimigo!")
# Criar o personagem
nome, classe, vida, mana = criar_personagem()
# Combate
vida_inimigo = 100
while vida > 0 and vida_inimigo > 0:
acao = input("O que você deseja fazer? (atacar/defender): ")
if acao == "atacar":
atacar(vida_inimigo)
# ... outras ações
print("Fim do jogo!")
---------------------------------------------------- StdinNotImplementedErrorTraceback (most recent call last) Cell In[5], line 16 13 print("Você causou 10 de dano ao inimigo!") 15 # Criar o personagem ---> 16 nome, classe, vida, mana = criar_personagem() 18 # Combate 19 vida_inimigo = 100 Cell In[5], line 4, in criar_personagem() 3 def criar_personagem(): ----> 4 nome = input("Digite o nome do seu personagem: ") 5 classe = input("Digite a classe do seu personagem: ") 6 vida = 100 File c:\Users\00661711722\Documents\PBE_24.2_8002\.venv\Lib\site-packages\ipykernel\kernelbase.py:1281, in Kernel.raw_input(self, prompt) 1279 if not self._allow_stdin: 1280 msg = "raw_input was called, but this frontend does not support input requests." -> 1281 raise StdinNotImplementedError(msg) 1282 return self._input_request( 1283 str(prompt), 1284 self._parent_ident["shell"], 1285 self.get_parent("shell"), 1286 password=False, 1287 ) StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
### 2. Programação Orientada a Objetos
class Personagem:
def __init__(self, nome, classe, vida=100, mana=50):
self.nome = nome
self.classe = classe
self.vida = vida
self.mana = mana
def atacar(self, inimigo):
dano = 10
inimigo.vida -= dano
print(f"{self.nome} causou 10 de dano a {inimigo.nome}!")
# Criar os personagens
jogador = Personagem("Herói", "Guerreiro")
inimigo = Personagem("Minotauro", "Bárbaro")
# Combate
while jogador.vida > 0 and inimigo.vida > 0:
acao = input("O que você deseja fazer? (atacar/defender): ")
if acao == "atacar":
jogador.atacar(inimigo)
# ... outras ações
print("Fim do jogo!")
---------------------------------------------------- StdinNotImplementedErrorTraceback (most recent call last) Cell In[6], line 21 19 # Combate 20 while jogador.vida > 0 and inimigo.vida > 0: ---> 21 acao = input("O que você deseja fazer? (atacar/defender): ") 22 if acao == "atacar": 23 jogador.atacar(inimigo) File c:\Users\00661711722\Documents\PBE_24.2_8002\.venv\Lib\site-packages\ipykernel\kernelbase.py:1281, in Kernel.raw_input(self, prompt) 1279 if not self._allow_stdin: 1280 msg = "raw_input was called, but this frontend does not support input requests." -> 1281 raise StdinNotImplementedError(msg) 1282 return self._input_request( 1283 str(prompt), 1284 self._parent_ident["shell"], 1285 self.get_parent("shell"), 1286 password=False, 1287 ) StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
Análise:
- Procedural: O código é dividido em funções que realizam tarefas específicas. Os dados do personagem são armazenados em variáveis globais ou passados como argumentos para as funções.
- Orientado a Objetos: O personagem é representado por uma classe
Personagem
, com atributos e métodos. As ações do personagem são realizadas através dos métodos da classe.
Vantagens da POO neste exemplo:
- Reutilização de código: A classe
Personagem
pode ser reutilizada para criar outros personagens com diferentes atributos. - Organização: O código fica mais organizado e fácil de entender, pois cada parte do personagem (atributos e ações) está encapsulada na classe.
- Extensibilidade: É mais fácil adicionar novas funcionalidades ao jogo, como novas classes de personagens ou habilidades.
Considerações:
- Complexidade: Para jogos mais complexos, a POO é essencial para gerenciar a quantidade de dados e interações entre os objetos.
- Aprendizado: A POO pode exigir um pouco mais de tempo para aprender, mas os benefícios a longo prazo são significativos.
Próximos passos:
- Expansão do jogo: Adicione mais classes, como itens, magias e inimigos.
- Interface gráfica: Utilize uma biblioteca como Pygame para criar uma interface gráfica para o jogo.
- Sistema de combate: Implemente um sistema de combate mais complexo, com diferentes tipos de ataques e defesas.
Possíveis tópicos:
- Herança: Crie classes derivadas de
Personagem
para representar diferentes tipos de personagens (mago, arqueiro, etc.). - Encapsulamento: Proteja os atributos da classe para evitar modificações indesejadas.
Lembre-se: A escolha entre programação procedural e orientada a objetos depende do contexto e da complexidade do problema. Em muitos casos, a combinação de ambos os paradigmas pode levar a soluções mais elegantes e eficientes.
class Personagem:
def __init__(self, nome, classe, vida=100, mana=50):
self.nome = nome
self.classe = classe
self.vida = vida
self.mana = mana
def atacar(self, inimigo):
dano = 10
inimigo.vida -= dano
print(f"{self.nome} causou {dano} de dano a {inimigo.nome}!")
class Mago(Personagem):
def __init__(self, nome):
super().__init__(nome, "Mago", mana=100)
def lançar_magia(self, inimigo):
if self.mana >= 20:
dano = 15
self.mana -= 20
inimigo.vida -= dano
print(f"{self.nome} lançou uma magia, causando {dano} de dano a {inimigo.nome}!")
else:
print(f"{self.nome} não tem mana suficiente para lançar uma magia.")
class Arqueiro(Personagem):
def __init__(self, nome):
super().__init__(nome, "Arqueiro")
def atirar(self, inimigo):
dano = 12
inimigo.vida -= dano
print(f"{self.nome} atirou uma flecha, causando {dano} de dano a {inimigo.nome}!")
# Como usar
mago = Mago("Gandalf")
arqueiro = Arqueiro("Legolas")
mago.lançar_magia(inimigo)
arqueiro.atirar(inimigo)
Gandalf lançou uma magia, causando 15 de dano a Minotauro! Legolas atirou uma flecha, causando 12 de dano a Minotauro!
Protegendo os Atributos da Classe Personagem¶
A proteção dos atributos é fundamental para garantir a integridade dos dados e evitar comportamentos inesperados em nossos programas.
Como aplicar o encapsulamento na classe Personagem:
Vamos adaptar o código da classe Personagem para proteger os atributos vida
e mana
, permitindo apenas modificações através dos métodos set_vida
e set_mana
. Isso nos dará controle sobre como esses valores são alterados, evitando que sejam atribuídos valores inválidos ou inconsistentes.
class Personagem:
def __init__(self, nome, classe, vida=100, mana=50):
self._nome = nome
self._classe = classe
self._vida = vida
self._mana = mana
def get_nome(self):
return self._nome
def get_classe(self):
return self._classe
def get_vida(self):
return self._vida
def set_vida(self, nova_vida):
if nova_vida >= 0:
self._vida = nova_vida
else:
print("A vida não pode ser negativa.")
def get_mana(self):
return self._mana
def set_mana(self, nova_mana):
if nova_mana >= 0:
self._mana = nova_mana
else:
print("A mana não pode ser negativa.")
def atacar(self, inimigo):
dano = 10
inimigo.set_vida(inimigo.get_vida() - dano)
print(f"{self.get_nome()} causou {dano} de dano a {inimigo.get_nome()}!")
def receber_dano(self, dano):
nova_vida = self.get_vida() - dano
self.set_vida(nova_vida)
Analisando as mudanças:
- Atributos privados: Os atributos
_vida
e_mana
agora são precedidos por um underscore, indicando que são privados e devem ser acessados apenas através dos métodos getters e setters. - Métodos getters e setters: Foram adicionados métodos
get_vida
,set_vida
,get_mana
eset_mana
para controlar o acesso aos atributos. - Validação: Os métodos
set_vida
eset_mana
verificam se o novo valor é válido antes de atribuí-lo ao atributo. - Método receber_dano: Foi criado um método
receber_dano
para encapsular a lógica de redução da vida do personagem, utilizando os métodos getters e setters.
Vantagens do encapsulamento:
- Integridade dos dados: Evita que a vida ou a mana do personagem sejam definidas como valores negativos.
- Flexibilidade: Permite adicionar mais validações ou lógica aos métodos getters e setters no futuro, sem afetar o código que utiliza a classe.
- Manutenibilidade: Facilita a manutenção do código, pois as regras de negócio estão centralizadas nos métodos getters e setters.
- Segurança: Protege os dados internos da classe de modificações acidentais.
Exemplo de uso:
# Como usar
personagem = Personagem("Herói", "Guerreiro")
print(personagem.get_vida()) # Imprime: 100
# Tentativa de modificar a vida diretamente (não recomendado)
# personagem._vida = -10 # Isso causaria um erro
# Forma correta de modificar a vida
personagem.set_vida(80)
print(personagem.get_vida()) # Imprime: 80
100 80
Próximos passos:
- Mais validações: Adicione mais validações aos métodos getters e setters para garantir a consistência dos dados.
- Eventos: Implemente um sistema de eventos para notificar quando a vida ou a mana de um personagem atingem um determinado valor.
- Propriedades: Explore o uso de propriedades em Python para tornar o acesso aos atributos mais intuitivo.
Com o encapsulamento, você terá um código mais robusto, seguro e fácil de manter.