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! 🥒🥒


