-

Ao estudar diferentes linguagens de programação ao longo dos anos, fui atraído pela família Lisp devido à sua simplicidade e poder. Lisp, criado por John McCarthy em 1958, é conhecido por suas características distintas, como a notação de código como listas de dados (daí o nome LISt Processing) e sua capacidade de manipular código como uma estrutura de dados. Emacs Lisp e Common Lisp são dois dos muitos dialetos que evoluíram a partir do Lisp original, cada um com suas próprias peculiaridades e casos de uso.

-

Minha jornada com Lisp levou-me ao Clojure pela conveniência de rodar na JVM, uma vez que Clojure é um moderno dialeto Lisp que roda na Java Virtual Machine (JVM). Optei pelo Clojure porque já tinha uma base sólida em Java, e Clojure oferece interoperabilidade sem emenda com código Java. Esta transição também foi influenciada pela minha crescente frustração com algumas complexidades encontradas na programação orientada a objetos (OOP), especialmente em Java.

-

Ao longo dos anos, percebi que a construção de hierarquias de classes complexas muitas vezes introduz uma complexidade desnecessária quando se trata de programação orientada a objetos (POO/OOP). Esse tipo de complexidade, especialmente em linguagens como Java, advém não dos desafios inerentes ao problema a ser resolvido, mas das metodologias e tecnologias escolhidas para o desenvolvimento. Para ilustrar, considere o processo de montar um modelo de avião a partir de um kit de modelagem.

-

O objetivo final é simples: construir um modelo que se pareça com um avião. No entanto, se o kit contém peças que se encaixam de maneiras específicas e exige um conjunto complexo de instruções para cada passo, o processo se torna desnecessariamente complicado. Isso se compara à maneira como a programação orientada a objetos pode obrigar nós, os desenvolvedores a seguir protocolos rígidos e a usar padrões que não necessariamente se alinham com as necessidades simples do problema, resultando em complexidade acidental e aumento do esforço de desenvolvimento.

-

Tal como a montagem de um modelo de avião poderia ser simplificada com um design mais intuitivo e menos peças, a programação poderia beneficiar-se de abordagens que reduzam a rigidez e a complexidade não essencial.

-

Este exemplo tenta capturar a ideia de que complexidades adicionais podem surgir não por causa do problema em si, mas devido às ferramentas e métodos escolhidos para abordá-lo. Essa complexidade se manifesta através de práticas comuns que podem complicar o código desnecessariamente. Por exemplo, a proliferação de POJOs (Plain Old Java Objects) e códigos boilerplate é notória.

-

Os POJOs, simples em uso, podem levar ao excesso de classes que servem principalmente para armazenar e recuperar dados sem conter métodos significativos. O código boilerplate, repetido em diversas partes do aplicativo, inclui a implementação frequente de métodos como get e set, hashCode, equals e toString, que, apesar de necessários, podem obscurecer a lógica principal do código e aumentar a carga de manutenção.

+

Ao estudar diferentes linguagens de programação, fui atraído pela família Lisp devido à sua simplicidade e poder. Lisp, criado por John McCarthy em 1958, é conhecido pela notação de código como listas de dados e pela capacidade de manipular código como uma estrutura de dados. Emacs Lisp e Common Lisp são dois dos muitos dialetos que evoluíram a partir do Lisp original, cada um com suas próprias peculiaridades e casos de uso.

+

Minha jornada com Lisp levou-me ao Clojure, um moderno dialeto que roda na Java Virtual Machine (JVM). Escolhi o Clojure por causa da interoperabilidade com código Java, já que eu tinha uma base sólida em Java. Esta transição foi também influenciada pela frustração com as complexidades da programação orientada a objetos (OOP) em Java.

+

Percebi que a construção de hierarquias de classes complexas frequentemente introduz uma complexidade desnecessária na OOP. Essa complexidade, especialmente em Java, surge mais das metodologias e tecnologias escolhidas do que dos desafios inerentes ao problema a ser resolvido. Por exemplo, montar um modelo de avião a partir de um kit pode ser simplificado com um design mais intuitivo e menos peças, semelhante à forma como a programação pode se beneficiar de abordagens que reduzem a rigidez e a complexidade não essencial.

+

Complexidades adicionais surgem não por causa do problema em si, mas devido às ferramentas e métodos escolhidos. Isso se manifesta através de práticas que complicam desnecessariamente o código, como a proliferação de POJOs (Plain Old Java Objects) e códigos boilerplate. POJOs, embora simples em uso, podem resultar em excesso de classes que servem principalmente para armazenar e recuperar dados. O código boilerplate, repetido em várias partes do aplicativo, inclui a implementação frequente de métodos como get e set, hashCode, equals e toString, que obscurecem a lógica principal do código e aumentam a carga de manutenção.

Considere o exemplo clássico abaixo na classe Employee abaixo, em Java, que exemplifica essa questão:

 1public class Employee {
  2    private String name;
@@ -204,15 +201,16 @@ 

Java e Clojure

20 21(defn set-employee-department [employee new-department] 22 (assoc employee :department new-department)) -

Aqui, defrecord cria uma estrutura de dados com campos nomeados, que também gera funções para acessar e modificar esses campos, promovendo a imutabilidade. Isso reduz a probabilidade de erros comuns em programas, como alterações de estado não intencionais, e simplifica o entendimento, teste e manutenção do código.

-

Essa abordagem minimiza a cerimônia e o boilerplate típicos da OOP em Java, focando mais nos dados e comportamentos reais do que na estrutura de classes. Ou seja, reduz a complexidade estrutural e foca em resolver o problema da forma mais transparente e segura possível. Além disso o defrecord também implementa automaticamente interfaces para serialização e outras funcionalidades, oferecendo mais do que apenas uma simplificação do acesso aos dados.

-

E aqui é essencial reconhecer que muito depende das escolhas e habilidades do desenvolvedor. No entanto, dentro de um consenso geral, a própria linguagem, juntamente com seus frameworks, bibliotecas e a filosofia subjacente da POO, tendem a conduzir os desenvolvedores por esse caminho de complexidade aumentada. Java, como uma linguagem projetada com uma forte inclinação para a POO, encoraja a criação de extensas hierarquias de classes e o uso de padrões de design que, embora úteis em muitos contextos, podem também adicionar camadas de complexidade que não são diretamente pertinentes à lógica de negócios em questão.

-

Os frameworks Java, como Spring e Hibernate, por exemplo, oferecem poderosas ferramentas de abstração que simplificam o desenvolvimento em muitos aspectos, mas que também podem levar a um código altamente acoplado e difícil de gerenciar se não forem usados com discernimento. Os POJOs e o código boilerplate, como já discutido, são exemplos claros de como as práticas comuns em Java podem contribuir para o aumento considerável dessa complexidade.

+