Todo mundo quer uma API extensível
Autor: Marcio Torres | Publicado: 2011-10-20T20:08:00-02:00

Como programador Java mas entusiasta de novas linguagens, acabo por sentir falta de alguns recursos que facilitam muito o trabalho, ou que pelo menos para mim fazem mais sentido usar.


Posso começar com um estudo de caso simples: dada uma String retornar o número de palavras.


É assim mesmo, simples assim, inclusive muito fácil de implementar em qualquer linguagem, mas a questão não é essa, como desenvolvedor eu quero usar orientação a objetos no projeto.


Vamos a uma implementação tosca em Java:


String frase = "Uma frase  de   exemplo";
System.out.println(frase.trim().split("\\s+").length);


Deu. Mostra 4. Entretanto eu quero reutilizar esta funcionalidade então em componentizo, bem, mas como?


Posso usar outro estudo de caso para dramatizar mais a história: verificar se uma String está em branco, ou seja, semelhante ao vazio só que ignorando espaços.


A implementação é medíocre:


String texto = "    ";
if (texto.trim().length() == 0) {
    System.out.println("vazio");
}


Não obstante, como no primeiro caso, eu quero componentizar.


No Java, é clássico fazer uma classe utilitária, algo como TipoUtils, neste caso, StringUtils:


public class StringUtils {
    public static int words(String str) {
        return str.trim().split("\\s+").length;
    }

    public static boolean isBlank(String str) {
        return str.trim().length() == 0;
    }
}


E uso assim:


String texto = "    ";
if (StringUtils.isBlank(texto)) {
    System.out.println("vazio");
}


Ainda posso usar um recurso introduzido no Java 5, o import estático.


import static meu.framework.utils.StringUtils.*;
// algumas linhas abaixo
String texto = "    ";
if (isBlank(texto)) {
    System.out.println("vazio");
}


Qual o problema com esta implementação? É que ela soa totalmente procedural em contraste ao modelo OO escolhido, em outras palavras eu queria fazer isso:


String texto = "    ";
// quero o próprio objeto me diga se está em branco
if (texto.isBlank()) {
    System.out.println("vazio");
} else {
     // quero que o próprio objeto me diga quantas palavras tem
    System.out.println("Existem " + texto.words() + " palavra(s)");
}


Em Java, impossível. No Java a classe String é final, ou seja, não pode ser estendida.


Para constar, uma extensão da API base do Java já existe e é distribuída pelo projeto commons do Apache e uma classe StringUtils já existe para estender as funcionalidades relacionadas a Strings e é muito bem escrita.


Mas isso não me deixa contente por que outras linguagens lidam muito melhor com esta situação, dando melhor suporte para estender a API base.


Vamos ao mesmo exemplo usando C#. No C# é possível, de certo modo, estender qualquer classe, mesmo as finais (sealed em C#), usando somente a API pública da classe, que já é suficiente para realizar muitas proezas:

public static class StringUtils {
 public static int Words(this string str) {
  return Regex.Split(str.Trim(), "\\s+").Length;
 }
 public static bool IsBlank(this string str) {
  return str.Trim().Length == 0;
 }
}

Implementações de classes estáticas no C# permitem estender a funcionalidade qualquer classe. Na verdade é um açúcar sintático no qual ele passa o conteúdo da variável para o método estático, ainda sendo procedural, mas de melhor leitura e mais elegante que em Java. Eu posso chamá-lo assim:

string texto = "    ";
if (texto.IsBlank()) {
    Console.WriteLine("vazio");
} else {
    Console.WriteLine("Existem " + texto.Words() + " palavra(s)");
}

Bem mais OO não acha?

Pode ficar ainda melhor em Ruby, onde as classes são abertas. Em outras palavras, é possível estender a funcionalidade até mesmo das classes básicas como String e FixNum (inteiros). Claro que esta liberdade deve ser usada com parcimônia, é bem mais fácil fazer caca, mas com responsabilidade dá para fazer isso:

class String
    def words
        self.split.length
    end
    def blank?
        self.strip.length == 0
    end
end

Então eu posso usar estes métodos assim:

texto = "uma frase"
print texto.words
print "vazio" if texto.blank?

Sinceramente, sinto falta dessa flexibilidade em Java.


Acho exagerado a recomendação do Pragmatic Programmers de que todo programador deveria aprender uma linguagem por ano, por outro lado entendo o motivo que é propiciar novas visões, pontos de vista, ser mais crítico ao seu código, saber que há alternativas para fazer, melhor ou pior, aquilo que tu já faz todo o dia.