DEV Community

Cover image for [PT-BR] Functional vs OOP: Uma análise profunda dos paradigmas de programação
André Borba
André Borba

Posted on

[PT-BR] Functional vs OOP: Uma análise profunda dos paradigmas de programação

Introdução

No universo da programação, poucos debates são tão acalorados quanto a discussão entre Programação Funcional (FP) e Programação Orientada a Objetos (OOP). Como um desenvolvedor com experiência em ambos os paradigmas, principalmente através de Clojure e TypeScript, quero compartilhar uma análise aprofundada que vai um pouco além das comparações superficiais.

O Estado: O Centro da Discussão

O gerenciamento de estado é possivelmente a maior diferença entre estes paradigmas. Vamos analisar como cada um lida com esta questão fundamental.

OOP: Estado Mutável e Encapsulamento

Em OOP, objetos encapsulam estado e comportamento. Considere este exemplo em TypeScript:

class BankAccount {
  private balance: number;

  constructor(initialBalance: number) {
    this.balance = initialBalance;
  }

  deposit(amount: number): void {
    this.balance += amount;
  }

  withdraw(amount: number): boolean {
    if (this.balance >= amount) {
      this.balance -= amount;
      return true;
    }
    return false;
  }

  getBalance(): number {
    return this.balance;
  }
}

const account = new BankAccount(1000);
account.deposit(500);    // Estado mutável
account.withdraw(200);   // Mais mutação
Enter fullscreen mode Exit fullscreen mode

Este código aparentemente simples esconde armadilhas:

  1. O estado pode ser modificado por qualquer método
  2. O rastreamento de mudanças é complexo
  3. O teste requer setup elaborado de estado

FP: Imutabilidade e Transparência

Agora, vejamos a mesma lógica em Clojure:

(defn make-account [initial-balance]
  {:balance initial-balance})

(defn deposit [account amount]
  {:balance (+ (:balance account) amount)})

(defn withdraw [account amount]
  (if (>= (:balance account) amount)
    {:balance (- (:balance account) amount)}
    account))

;; Uso
(def account (make-account 1000))
(def after-deposit (deposit account 500))
(def after-withdraw (withdraw after-deposit 200))
Enter fullscreen mode Exit fullscreen mode

Observem as diferenças fundamentais:

  1. Cada operação retorna um novo estado
  2. Não há mutação
  3. O fluxo de dados é explícito e rastreável

Composição vs Herança

OOP: Hierarquia e Acoplamento

A herança em OOP frequentemente leva a hierarquias rígidas:

abstract class Vehicle {
  protected speed: number = 0;

  abstract accelerate(): void;
}

class Car extends Vehicle {
  accelerate(): void {
    this.speed += 10;
  }
}

class Bicycle extends Vehicle {
  accelerate(): void {
    this.speed += 2;
  }
}
Enter fullscreen mode Exit fullscreen mode

Problemas:

  1. Acoplamento forte entre classes
  2. Hierarquias difíceis de modificar
  3. Comportamento compartilhado através de herança

FP: Composição e Transformação

Em FP, favorecemos composição sobre herança:

(defn create-vehicle [type initial-speed]
  {:type type
   :speed initial-speed})

(defn accelerate-car [vehicle]
  (update vehicle :speed #(+ % 10)))

(defn accelerate-bicycle [vehicle]
  (update vehicle :speed #(+ % 2)))

;; Composição de funções
(def vehicle-operations
  {:car accelerate-car
   :bicycle accelerate-bicycle})

(defn accelerate [vehicle]
  ((get vehicle-operations (:type vehicle)) vehicle))

;; Uso
(-> (create-vehicle :car 0)
    accelerate
    accelerate)
Enter fullscreen mode Exit fullscreen mode

Benefícios:

  1. Funções puras e composíveis
  2. Sem acoplamento
  3. Fácil de testar e modificar

Por Que FP é Frequentemente Superior?

  1. Raciocínio Local: Em FP, uma função é uma transformação pura de input para output. Não há efeitos colaterais escondidos.

  2. Concorrência: Estruturas imutáveis são naturalmente thread-safe:

;; Clojure - Thread safe por design
(def numbers (range 1000))
(def result 
  (->> numbers
       (partition-all 100)
       (pmap #(reduce + %))
       (reduce +)))
Enter fullscreen mode Exit fullscreen mode
  1. Testabilidade: Funções puras são triviais de testar:
(deftest withdraw-test
  (testing "successful withdrawal"
    (is (= {:balance 800}
           (withdraw {:balance 1000} 200))))

  (testing "insufficient funds"
    (is (= {:balance 100}
           (withdraw {:balance 100} 200)))))
Enter fullscreen mode Exit fullscreen mode

Conclusão

Enquanto OOP tem seu lugar, especialmente em domínios onde o encapsulamento de estado é crucial, FP oferece vantagens significativas:

  • Código mais previsível e fácil de rastrear
  • Melhor suporte para concorrência
  • Testabilidade superior
  • Composição mais flexível

A indústria está gradualmente percebendo estas vantagens, como evidenciado pela adoção crescente de conceitos funcionais em linguagens tradicionalmente OOP. TypeScript, por exemplo, incorpora cada vez mais padrões funcionais através de tipos algebráicos e funções de ordem superior.

Se você está começando agora, considere investir tempo em aprender FP. Não apenas expandirá seus horizontes, mas também o fará questionar muitas práticas que tomamos como certas na programação orientada a objetos.

Recursos Adicionais

Top comments (0)