Ir para o conteúdo principal
Background Image

Do Sleep Sort ao IIFE - Exemplos de Design de Código Criativo

··7 minutos·
Rafael Issao
Autor
Rafael Issao
Apaixonado por tecnologia, programação e inovação. Adoro compartilhar conhecimento e aprender coisas novas todos os dias.
Tabela de conteúdos

Desenvolvimento de software e Design de Código exige criatividade.

Criatividade para identificar problemas e criar soluções de qualidade.

Vou compartilhar designs de código criativos, inusitados e brilhantes que encontrei ao longo da minha carreira – e que transformaram minha forma de enxergar a programação.

Sleep Sort
#

Quando aprendemos algoritmos, conhecemos muitos algoritmos de ordenação.

Mas será que você consegue pensar em um algoritmo de ordenação onde você não compara e nem conta o inteiro?

Sleep Sort é capaz de fazer isso.

Sleep Sort é um algoritmo engraçado, criativo e parece ser O(n)!

Veja o código:

var unsorted = [3, 5, 2, 1, 4]

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    unsorted.each { number ->
        executor.submit() {
            Thread.sleep(number * 100)
            println(number)
        }
    }
}

A ideia é simples.

Para cada inteiro, damos um sleep onde o tempo é o valor do inteiro.

Ou seja, o inteiro com menor valor fica menos tempo no modo sleep e imprime aquele número!

E a saída fica com o inteiro ordenado!

Provavelmente não é útil, mas eu me apaixonei pela simplicidade e criatividade do design do algoritmo.

Só não use este algoritmo em produção por favor!

Refatoração diferente
#

Eu já pratiquei muitas refatorações com TDD.

E posso dizer com confiança e competência que a maior dificuldade de refatorar um código é a criatividade para pensar em um bom design.

Mesmo que você saiba Design Patterns e Clean Code, o mesmo padrão pode ser implementado de formas diferentes.

E existem situações em que Design Patterns e Clean Code não ajudam na refatoração pois são padrões para problemas contextuais e gerais.

Vamos tentar refatorar um código que resolve um problema clássico de programação:

Transformar um número arábico para romano.

Quando encontrei este problema a minha primeira solução foi algo assim:

def numberToRoman(int number) {
    def result = ""

    while (number >= 1000) {
        result += "M"
        number -= 1000;
    }
    while (number >= 900) {
        result += "CM"
        number -= 900;
    }
    while (number >= 500) {
        result += "D"
        number -= 500;
    }
    while (number >= 400) {
        result += "CD"
        number -= 400;
    }
    while (number >= 100) {
        result += "C"
        number -= 100;
    }
    while (number >= 90) {
        result += "XC"
        number -= 90;
    }
    while (number >= 50) {
        result += "L"
        number -= 50;
    }
    while (number >= 40) {
        result += "XL"
        number -= 40;
    }
    while (number >= 10) {
        result += "X"
        number -= 10;
    }
    while (number >= 9) {
        result += "IX"
        number -= 9;
    }
    while (number >= 5) {
        result += "V"
        number -= 5;
    }
    while (number >= 4) {
        result += "IV"
        number -= 4;
    }
    while (number >= 1) {
        result += "I"
        number -= 1;
    }
    return result
}

É simples de entender mas trabalhoso de escrever.

Se quisermos reescrever este método, podemos perceber que existe uma repetição do bloco while com o símbolo e o número que o símbolo representa:

def romanMap = [
    1000: "M",
    900: "CM", 500: "D", 400: "CD", 100: "C",
    90: "XC", 50: "L", 40: "XL", 10: "X",
    9: "IX", 5: "V", 4: "IV", 1: "I"
]

def numberToRoman2(int number) {
    def result = ""

    romanMap.each { divisor, symbol ->
        result += symbol * (number / divisor)
        number %= divisor
    }

    return result
}

Legal! A refatoração manteve o DRY e ficou fácil de adicionar uma representação romana para, por exemplo, 5000.

Mas e se existisse uma outra solução sem fazer somas, restos e divisões?

Existe! Olha o seguinte código:

def numberToRoman3(int number) {
    return ('I' * number)
            .replace('IIIII', 'V')
            .replace('IIII', 'IV')
            .replace('VV', 'X')
            .replace('VIV', 'IX')
            .replace('XXXXX', 'L')
            .replace('XXXX', 'XL')
            .replace('LL', 'C')
            .replace('LXL', 'XC')
            .replace('CCCCC', 'D')
            .replace('CCCC', 'CD')
            .replace('DD', 'M')
            .replace('DCD', 'CM')

A ideia deste código é realizar a conversão por substituição.

Por exemplo, o número 14 deve ter o símbolo ‘I’ 14 vezes:

IIIIIIIIIIIIII

Agora, substituímos cinco ‘I’s por um ‘V’:

VIIIIIIIIII

No próximo passo, substituímos quatro ‘I’s por um ‘IV’:

XIV

E por fim, trocamos dois ‘V’ por um X:

XIV

E temos o resultado!

Gosto muito deste código pois me lembra a importância de pensar de um ponto de vista diferente para resolver o problema.

As duas primeiras versões utilizamos a matemática para resolver o problema.

A última solução é um problema de contagem e substituição de strings.

Você tem pensado em vários design de código diferente para o mesmo problema?

Bloco de código como objeto de POO
#

Este é um design de código onde a ideia é antiga mas ainda assim, mesmo em 2025, muita gente não conhece.

Sabemos que em uma linguagem orientada a objetos, trabalhamos com objetos e métodos.

Mas poucos sabem que, em uma linguagem puramente orientada a objetos, um bloco de código é um objeto!

Isto significa que blocos de códigos podem ter métodos, podem ser passados como parâmetros e também guardar a referência de um bloco de código em uma variável!

Um exemplo simples:

class CodeBlocksAsObjects {

    static void main(String[] args) {
        // Blocos de código em variáveis
        def helloWorldCodeBlock = { String name -> "Hello World, $name!"}
        def happyBirthdayCodeBlock = { String name -> "Happy Birthday, $name"}

        // Passando blocos de código como parâmetro
        printMessageFor('Issao', helloWorldCodeBlock)
        printMessageFor('Issao', happyBirthdayCodeBlock)

        // É possível passar um bloco de código direto como parâmetro
        printMessageFor('Issao', { String name -> "Hello, $name" })

        // É o mesmo código acima, mas em groovy, quando o último parâmetro é uma função,
        // podemos deixar o bloco de código fora do parênteses.
        printMessageFor('Issao') { String name -> "Hello, $name" }

        // Se o bloco de código só tem um parâmetro, tem uma variável especial
        // chamada "it" que possui o valor do parâmetro
        printMessageFor('Issao') { 'Hello, $it' }
    }

    static def printMessageFor(String name, Closure messageFunction) {
        // Utilizando o código!
        println messageFunction(name)
    }
}

E o build.gradle funciona assim!

Ele é um arquivo de configuração mas também é um código orientado a objetos!

// Sintaxe mais utilizada
dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter'
}

// O bloco de código é passado como parâmetro.
// Ou seja, "dependencies" e "testImplementation" é um método!
dependencies({
    testImplementation 'org.junit.jupiter:junit-jupiter'
})

Temos um design onde é possível criar uma DSL (Domain-Specific Language).

Normalmente eu escrevo os testes com Groovy e Spock. Pela facilidade de criar DSLs e conseguir ser bem expressivo nos testes.

IIFE com Javascript
#

O último exemplo é com Javascript.

Antes de termos os famosos frameworks, eu escrevia código web basicamente utilizando o javascript puro com algumas bibliotecas como jQuery e Prototype.

E para organizar o código javascript, surgiu este Design Pattern chamado de IIFE.

Do glossário do MSDN Web Docs:

IIFE (Immediately Invoked Function Expression) é uma função em Javascript que é executada assim que definida.

É um Design Pattern também conhecido como Self-Executing Anonymous Function e contém duas partes principais. A primeira é a função anônima cujo escopo léxico é encapsulado entre parênteses. Isso previne o acesso externo às variáveis declaradas na IIFE, bem como evita que estas variáveis locais poluam o escopo global.

Este padrão é interessante pois é possível deixar o código javascript com alta coesão.

Veja o seguinte exemplo:

var counter = (function(){
    var i = 0;

    return {
        get: function(){
            return i;
        },
        set: function(val){
            i = val;
        },
        increment: function() {
            return ++i;
        }
    };
})();

counter.get();
counter.set(3);
counter.increment();
counter.increment();

Conseguimos encapsular as funções e o estado em um único lugar!

E ainda tem mais! Podemos injetar dependências com IIFE:

var counter = (function(jQuery) { // Recebemos a dependência aqui

    // Podemos utilizar jQuery
    return {};
})(jQuery)); // Podemos passar as dependências aqui

Era possível modularizar o código Javascript antes de termos todo recurso que temos hoje!

A ideia de utilizar funções anônimas e já invocá-las para criar um escopo fechado e com estado me faz pensar: Será que precisamos de frameworks e bibliotecas para termos um design de qualidade?

Conclusão
#

A criatividade não é sobre “quebrar regras”, mas sobre enxergar possibilidades onde outros veem limites.

Padrões de design e boas práticas são guias, mas a inovação nasce quando:

  • Adaptamos conceitos de áreas improváveis (como threads para ordenação).
  • Redefinimos o escopo do problema (transformar cálculos em substituição de strings).
  • Aproveitamos recursos pouco explorados das linguagens (bloco de código como objetos).

Então, da próxima vez que enfrentar um desafio técnico, pergunte-se:

“Será que existe uma outra solução?”

E você? Conhece um design de código interessante, diferente ou inusitado?

Compartilha nos comentários!

🥒🥒 Espero que tenham curtido e até o próximo artigo! 🥒🥒