Mostrando postagens com marcador açúcar sintático. Mostrar todas as postagens
Mostrando postagens com marcador açúcar sintático. Mostrar todas as postagens

20 de out. de 2011

Todo mundo quer uma API extensível

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.

18 de out. de 2011

O operador 'Elvis'

O açúcar não deve ser consumido em excesso e nem deixar de ser consumido, assim como o sal, a gordura, etc.
Não, não virei nutricionista. É que este post é para falar de açúcar sintático, syntactic sugar em inglês, que é, em poucas palavras, o uso de operadores e construções da linguagem reduzem a escrita de um código maior e, assim como tudo, não pode deixar de existir na linguagens modernas e ao mesmo tempo não deve ser exagerado.

Nem sempre o açúcar sintático simplifica, pois às vezes tornam a leitura mais difícil, mas por outro lado economizam tempo -e tempo é dinheiro.

Vou citar um exemplo clássico, o operador ternário "? :"
Quem aqui né amigo não teve que escrever um código assim:
if (atraso > 10) {
    multa = 12.4;
} else {
    multa = 0.0;
}
Embora o código esteja em Java a construção é inerente a qualquer linguagem.

O mesmo código pode ser escrito usando o operador ternário, que é um açúcar sintático pois na prática ele compila, ou interpreta, para o código acima. Exemplo:
multa = atraso > 10 ? 12.4 : 0.0;
As linguagens estão cada vez mais adicionando açúcar sintático e uma que simpatizo muito é Ruby, entretanto como desenvolvedor Java sinto falta de um especialmente: o operador Elvis!

O "Elvis" -não me pergunte por que chamam assim, só sei o que dizem: "Elvis não morreu"- é uma simplificação do operador ternário para verificação de nulos. Por exemplo, imagine que desejes atribuir o valor de um parâmetro a uma variável, entretanto caso este parâmetro seja nulo é necessário atribuir um valor padrão. Vou propor um estudo de caso e uma implementação fictícia no código abaixo usando o clássico "if":
if (param.get("action") != null) {
    acao = param.get("action");
} else {
    acao = "listar";
}
O mesmo código poderia ser escrito assim:
acao = param.get("action") != null ? param.get("action") : "listar";

Esta sintaxe com operador ternário simplifica bastante porém ainda é possível simplificar ainda mais usando, bem, o Elvis! Abaixo uma implementação fictícia usando C#:
acao = param["action"] ?? "listar";
Bem melhor não concorda?
Java está meio atrasado nesta corrida por facilitação de sintaxe, o operador Elvis estava previsto para ser inserido no Java 7 e não rolou. A princípio ele teria a mesma sintaxe do Groovy, assim:
acao = param.get("action") ?: "listar";
Em Ruby ele é diferente sendo assim:
acao = param[:action] || "listar"
E pior, atualmente até o PHP tem, a partir da versão 5.3.

Enfim, o Java mostra sinais de idade e espero sinceramente que a Oracle com o Java 8 adicione mais liberdade a linguagem sem perder a elegância, a tipagem forte e robustez que o Java tem.