O que é programação orientada a objetos?

Introdução

No dia-a-dia do desenvolvimento é comum ouvirmos o termo "Paradigma de programação", e por mais que você não o escute tanto no seu meio, o conhecimento sobre do que se trata esse termo é essencial.

Conheça-o um pouco melhor abaixo. Bem-vindo, internauta!

Conceito

Um paradigma é uma forma de lidar com um problema. Um paradigma de programação é uma forma de lidar com os problemas envolvendo programação. Vamos a um exemplo utilizando o mundo real:

Vida real

Suponhamos que você precise levar um piano do térreo para o terceiro andar de um edifício. Você para e analisa de que formas pode realizar isso: Carregando-o no braço por vários lances de escada até o terceiro andar; levando-o em um carrinho de mão; desmontando-o na porta e montando todas as peças no final; colocando um motor no top do prédio que tracionaria o piano até o terceiro andar; chamando um guindaste; etc.

Como você pode ver, há varias forma de se resolver um mesmo problema, e na programação não é diferente. Vamos a um exemplo no universo da programação:

Programação

Com a diversidade de linguagens que encontramos disponíveis atualmente, temos uma quantidade satisfatória de paradigmas que nos são muito úteis a depender do problema que temos. Algumas linguagens resolvem problemas de front-end, outras de back-end, algumas ainda são fullstack; algumas são mais adequadas para desenvolvimento web, outras para plataforma Windows, outras ainda para a criação de compiladores; algumas são ótimas para agilizar o desenvolvimento do código, outras buscam ter uma escrita/sintaxe simples, outras ainda resolvem problemas de outras linguagens mais antigas.

Mas onde entra a orientação a objetos?

A orientação a objetos é um dos paradigmas mais utilizados hoje. Ela veio para otimizar o desenvolvimento de código dos sistemas atuais, que têm se tornado cada vez mais complexos e maiores.

Na sua abordagem consistem 4 conceitos fundamentais: abstração, herança, polimorfismo e encapsulamento.

Ao realizarmos a abstração de um objeto (também conhecido como classe) ao nosso código, estamos atribuindo a ele uma serie de características (também chamadas de parâmetros), ele pode executar ações (também chamadas de métodos) e interagir com outros objetos. Ou seja, na orientação a objetos criamos classes que possuem parâmetros e métodos, que podem interagir com outras classes.

Podemos criar classes dentro de classes (sub-classes). As classes superiores compartilham parâmetros e métodos com as sub-classes, e esse comportamento se chama herança. As sub-classes podem vir a ter novas características, ou mesmo alterar parâmetros ou métodos herdados, a depender do contexto em que são inseridos (o que se chama de polimorfismo).

Bem como, podemos adicionar uma camada de abstração entre a saída e entrada de dados (conhecida como encapsulamento). Por exemplo, ao adicionar um método que seja mediador entre aquilo que eu peço (utilizar os recursos do objeto) e aquilo que eu quero (valor do parâmetro ou o resultado de um método).

Quando encontramos essas 4 características fundamentais (abstração, herança, polimorfismo e encapsulamento) em um programa estamos lidando com uma aplicação orientada a objetos.

Além das características essenciais da orientação a objetos, temos algumas vantagens a mais que valem a pena serem citadas:

  • O sistema se parece com algo mais próximo ao que pode ser observado na vida real;
  • A reutilização de código é melhor feita, e hoje ela é essencial. Reusar o código faz com que um sistema tenha menos linhas e seja desenvolvido mais rapidamente;
  • Torna diferentes partes do código mais independentes umas das outras, como módulos;
  • Fácil leitura, manutenção e atualização;
  • Facilita a criação de bibliotecas.

Apesar de diversas vantagens, esse paradigma possui algumas desvantagens, sendo a mais latente a maior lentidão na execução do software, visto que, pelas inferências do próprio modelo, um código orientado a objetos faz muito mais desvios do que um código funcional. Porém, tal fraqueza é quase imperceptível graças ao grande poder computacional que temos hoje.

Exemplo prático

Para demonstrar as diferenças entre os dois modelos de programar, eu preparei 2 exemplos utilizando Python (linguagem multiparadigma, isto é: pode ser escrita em mais de uma forma) que mostraram como ambos os métodos encaram o mesmo problema.

Abaixo temos o exemplo de um programa de calculadora que utiliza uma programação funcional:

# ================== INÍCIO ===================
while True:

    # Mensagem de boas-vindas
    print('\
Inicie seus cálculos!\n\
=====================\n')

    # Define o primeiro numero da operação
    num1 = ''

    # Solicita que o usuário digite o número natural
    while not num1.isnumeric():
        num1 = input('1° Número: ')

        # Sai do código se o usuário digitar 's'
        if num1 == 's':
            exit()
    
    # Define a operação
    ope = ''

    # Solicita que o usuário digite uma operação válida
    while ope not in {'+','-','*','/'}:
        ope = input(f'Um operador(+,-,*,/):\n{num1} ')

        # Sai do código se o usuário digitar 's'
        if ope == 's':
            exit()
        
        # Mensagem de erro
        elif ope not in {'+','-','*','/'}:
            print("\nEntre com um operador válido, engraçadinho\n")

    # Define o segundo numero da operação
    num2 = ''

    # Solicita que o usuário digite o número natural
    while not num2.isnumeric():
        num2 = input(f'2° Número: {num1} {ope} ')

        # Sai do código se o usuário digitar 's'
        if num2 == 's':
            exit()

    # Converte as entradas numéricas para números de ponto flutuante
    num1 = float(num1)
    num2 = float(num2)

    # Realiza as operações e as mostra no console
    if ope == '+':
        print(f"\nResultado:", num1 + num2,"\n")
    elif ope == '-':
        print(f"\nResultado:", num1 - num2,"\n")
    elif ope == '*':
        print(f"\nResultado:", num1 * num2,"\n")
    elif ope == '/':
        print(f"\nResultado:", num1 / num2,"\n")
    else:
        pass
# ================== FIM ===================

Agora, veremos um código com o mesmo comportamento, porém orientado a objetos:

# ================== INÍCIO ===================

class Calculadora:  # Diz ao programa para criar um objeto nomeador Calculadora
    def __init__(self):  # Informa as características básicas do objeto (também chamado de Construtor)
        pass

    def run(self):  # Define um método (nosso algoritmo)
        while True:
            self.saudacao()

            verificado = False
            while verificado == False:
                termo = 1
                valor_para_avaliar = self.entrada(termo)
                verificado = self.verificador_numerico(valor_para_avaliar)

            num1 = verificado

            verificado = False
            while verificado == False:
                termo = 2
                valor_para_avaliar = self.entrada(termo)
                verificado = self.verificador_operacao(valor_para_avaliar)

            ope = verificado

            verificado = False
            while verificado == False:
                termo = 3
                valor_para_avaliar = self.entrada(termo)
                verificado = self.verificador_numerico(valor_para_avaliar)

            num2 = verificado

            if ope == '+':  # Verifica se é uma soma
                self.soma(num1,num2)
            elif ope == '-':  # Verifica se é uma subtração
                self.subtracao(num1,num2)
            elif ope == '*':  # Verifica se é uma multiplicação
                self.multiplicacao(num1,num2)
            elif ope == '/':  # Verifica se é uma divisão
                self.divisao(num1,num2)
            else:
                print("ERROR")
    
    def saudacao(self):  # Sauda ao usuário
        print('\
Inicie seus cálculos!\\n\
=====================\n')

    def entrada(self,termo):  # Recebe a entrada do usuário
        if termo == 1:
            valor_digitado = input("Digite um número ou [s]air: ")
        if termo == 2:
            valor_digitado = input("Escolha um operador (+,-,/,*) ou [s]air: ")
        if termo == 3:
            valor_digitado = input("Digite outro número ou [s]air: ")
        return valor_digitado

    def verificador_numerico(self, valor_entrado):  # Verifica se o valor digitado é numérico (Natural)
        if valor_entrado == 's':
            exit()
        elif valor_entrado.isnumeric():
            return float(valor_entrado)
        else:
            return False
    
    def verificador_operacao(self, valor_entrado):  # Verifica se o valor digitado é uma operação válida
        if valor_entrado == 's':
            exit()
        elif valor_entrado not in {'+','-','*','/'}:
            return False
        else:
            return valor_entrado

    def soma(self, valor_a, valor_b):  # Realiza a soma dos valores entrados
        return print(valor_a + valor_b)

    def subtracao(self, valor_a, valor_b):  # Realiza a subtração dos valores entrados
        return print(valor_a - valor_b)

    def multiplicacao(self, valor_a, valor_b):  # Realiza a multiplicação dos valores entrados
        return print(valor_a * valor_b)

    def divisao(self, valor_a, valor_b):  # Realiza a divisão dos valores entrados
        return print(valor_a / valor_b)

bolinha = Calculadora()  # Instância a classe
bolinha.run()  # Chama o método 'run()', que será o nosso algoritmo.

# ================== FIM ===================

Observações

  • Perceba que o código do estruturado a funções flui de cima para baixo, enquanto o orientado a objetos faz muito mais desvios;
  • Note que, no segundo código, todos os métodos estão muito bem definidos e separados, tornando-o mais organizado;
  • Verifique que cada método executa uma função específica, o que torna o objeto bem mais robusto;
  • Seria possível ainda agrupar métodos semelhantes criando sub-classes, o que tornaria o código bem mais organizado;
  • O único método utilizado pelo programa é o 'run()', mas poderíamos chamar muito mais métodos se escrevêssemos o algoritmo fora do objeto.

Conclusão

Como pudemos ver, a utilização do paradigma de orientação a objetos pode representar comportamentos simples como o de uma calculadora, porém o seu uso é muito mais aplicado a sistemas mais complexos, um sistema completo de gerencia de loja, por exemplo. Cada funcionário/função seria um objeto, cada função possui características especificas (atributos), e dentro do sistema pode realizar certas ações (métodos).

Enfim, eu espero que vocês tenham conseguido entender. Para qualquer dúvida, dica, sugestão ou crítica, por favor, utilize o campo dos comentários. Muito obrigado e até mais!

Referência:

https://pt.m.wikipedia.org/wiki/Paradigma_de_programação

https://blog.betrybe.com/tecnologia/paradigmas-de-programacao/

https://www.devmedia.com.br/os-4-pilares-da-programacao-orientada-a-objetos/9264

Comentários

Postagens mais visitadas deste blog

CI/CD/CD: Dev e Ops convivendo em harmonia

Introdução ao Strongswan