28 de maio de 2010

Pegadinha Java III

Os posts marcados como "Pegadinha Java" têm a intenção de testar a sagacidade dos meus alunos iniciantes na Programação em Java. São tópicos curiosos e divertidos, e não têm a intenção de medir o conhecimento na linguagem Java.

A vocês, meus alunos, lembrem-se que a idéia é bater o olho no código e apontar a resposta correta. Naturalmente, trata-se de pegadinhas, ou seja, são trechos de código bastante mal-intencionados... :-)

Nesta terceira questão, considere o seguinte trecho de código:
public class Executavel{
public static void main(String[] args){
http://java.sun.com
System.exit(0);
}
}
Você deve estar pensando (ou pelo menos deveria): - Ei, esta terceira linha de código não é uma instrução válida do Java!

Exatamente! No entanto, eu digo a você que este trecho compila e roda sem erros!

E, infelizmente, você não pode afirmar o contrário, pois o contrário nem existe entre as respostas válidas... :-)

Portanto, marque abaixo a única alternativa correta:

a - compila e roda sem erros, mas não acontece nada.
b - compila e roda sem erros, e abre o browser com a página da Sun.
c - não é possível que isto compile!! A 3ª linha nem é código-java.
d - se isto compilar e rodar sem erros, minha JVM ficou maluca!
e - tem certeza que isto compila???

E a alternativa correta é a letra.......................... A

Muito bem! A resposta correta, como vocês podem conferir na máquina de vocês, é: "Compila e roda sem erros, mas não acontece nada". Mas o quê eu queria saber mesmo é:

Por quê isto compila e roda sem erros, se a 3ª linha não é código java?

Abraços a todos e até o próximo post!

Pegadinha Java II

Os posts marcados como "Pegadinha Java" têm a intenção de testar a sagacidade dos meus alunos iniciantes na Programação em Java. São tópicos curiosos e divertidos, e não têm a intenção de medir o conhecimento na linguagem Java.

A vocês, meus alunos, lembrem-se que a idéia é bater o olho no código e apontar a resposta correta. Naturalmente, trata-se de pegadinhas, ou seja, são trechos de código bastante mal-intencionados... :-)

Nesta segunda questão, vocês devem se lembrar do trecho de código abaixo:

System.out.println("010" + 10)

Todos vão se lembrar que o resultado não vai ser 20 e sim 01010. A explicação é fácil, pois o primeiro fator da soma é um String, logo ele vai converter o segundo fator também para String e concatenar os dois fatores.

Mas e o techo abaixo:

System.out.println(010 + 010)

O primeiro fator é numérico, logo o resultado da soma vai ser...

Muito bem! 16 (!!!)

Por quê?

Àqueles que descobriram a resposta, tenham a gentileza de não estragar a surpresa... :-)

Só me enviem mensagens se tiverem dúvidas, mas não revelem a resposta para os demais, certo?

Um abraço e até a próxima!

Pegadinha Java I

Os posts marcados como "Pegadinha Java" têm a intenção de testar a sagacidade dos meus alunos iniciantes na Programação em Java. São tópicos curiosos e divertidos, e não têm a intenção de medir o conhecimento na linguagem Java.

A vocês, meus alunos, lembrem-se que a idéia é bater o olho no código e apontar a resposta correta. Naturalmente, trata-se de pegadinhas, ou seja, são trechos de código bastante mal-intencionados... :-)

Nesta primeira questão, pergunto: vocês sabem somar 21 + 5?

Bem simples, certo? Então me digam o que será mostrado no trecho de código abaixo:
public class Executavel{
public static void main(String [] args){
System.out.println(2l + 5);
}
}
Muito bem! O resultado não é 26. Mas, por quê não é 26?

Bom, esta é a resposta que eu espero de vocês, esforçados alunos! Copiem o código acima, colem no editor Java, compilem, rodem, e me digam por quê o resultado mostrado não foi 26. (*Lembrem-se, vocês têm que copiar e colar o código acima, não vale digitar vocês mesmos).

Àqueles que descobriram a resposta, tenham a gentileza de não estragar a surpresa... :-)

Só me enviem mensagens se tiverem dúvidas, mas não revelem a resposta para os demais, certo?

Um abraço e até a próxima!

Adapter

De acordo com GoF, Adapter serve para "converter a interface de uma classe em outra interface esperada pelos clientes. O Adapter permite que certas classes trabalhe em conjunto, pois de outra forma seria impossível por causa de suas interfaces incompatíveis".

Para exemplificar, vou exibir uma situação simples em que se pode implementar o Adapter. A idéia deste blog é fazer com que você possa entender como funcionam os Padrões, e a partir daí, possa utilizá-lo em sua situação prática.

No nosso exemplo, vamos imaginar que você tenha uma classe responsável por imprimir seus relatórios. O nome desta classe é PrintaRelatorio, e ela vai possuir um atributo chamado out do tipo PrintStream. É um objeto que todos os desenvolvedores Java conhecem bem, utilizado sempre que mostramos algo na tela com System.out.println("texto"); é o mesmo System.out que nossa classe vai utilizar. Ela terá um método chamado imprime que receberá uma String como parâmetro e imprimirá na tela o resultado.

O código completo de PrintaRelatorio.java será:
public class PrintaRelatorio {
private PrintStream out;

public PrintStream getOut(){
return this.out;
}
public void setOut(PrintStream out) {
this.out = out;
}
public void imprime(String texto){
getOut().println(texto);
}
}
Um atributo out do tipo PrintStream, seus getters e setters, e o método imprime() recebendo como parâmetro o texto a ser mostrado no console da aplicação com println(). Bastante simples!

Para utilizar a funcionalidade da classe acima, bastaria fazer em nossa classe Executável:
public static void main(String[] args) {
PrintaRelatorio printa = new PrintaRelatorio();
printa.setOut(System.out);
printa.imprime("texto");
}
O requisito do Cliente era apenas este: ter uma classe que permita imprimir no console uma informação. Está feito!

Entretanto, como todo analista sabe muito bem, um requisito serve para ser alterado pelo Cliente, várias vezes, durante todo o projeto!

Em nosso exemplo, o Cliente quer agora que a informação seja exibida em uma "telinha bonitinha" e não mais no console da linha de comando. Porém, a classe PrintaRelatorio já está homologada em produção, sendo utilizada por vários outros programas, permeada em diversos sistemas... e o fato é que você não vai poder mexer em PrintaRelatorio.java!

E assim surge o nosso primeiro problema: para exibir a informação na tela que o Cliente deseja, precisaremos utilizar JOptionPane.showMessageDialog() no lugar de System.out.println(). Porém, eu não posso fazer printaRelatorio.setOut(JOptionPane) porque este método espera algo do tipo PrintStream. Além do mais, o método imprime() de printaRelatorio invoca o println(), e eu preciso invocar showMessageDialog(). Como resolver isso sem alterar a classe PrintaRelatorio?

Resposta: aplicando o padrão Adapter. De novo, GoF: "converte a interface de uma classe em outra interface esperada pelos clientes". É a mesma situação que temos!

Vou criar uma classe chamada AdaptadoraParaPainel que vai estender PrintStream:

public class AdaptadoraParaPainel extends PrintStream

Ao fazer isso, ela me obriga a inserir um construtor para informar um Arquivo, visto que a especialidade dela é trabalhar com streams. Mas eu não vou trabalhar com arquivos! Então vou inserir o construtor que ela espera, declarar a exceção que ela espera, apenas para o compilador parar de reclamar. Ou seja:

//Construtor
public AdaptadoraParaPainel() throws FileNotFoundException{
.....super(new File("nada"));
}

Aí então vou inserir o método que a classe cliente espera que eu chame, com a mesma assinatura, mas invocando o mecanismo que eu preciso agora, que é showMessageDialog(). Isto é:

public void print(String texto){
.....JOptionPane.showMessageDialog(null,texto);
}

Portanto, assim fica o código completo de AdaptadoraParaPainel.java :
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import javax.swing.JOptionPane;

public class AdaptadoraParaPainel
extends PrintStream {

public AdaptadoraParaPainel()
throws FileNotFoundException{

super(new File("nada"));
}
public void print(String texto){
JOptionPane.showMessageDialog(null,texto);
}
}
Com isso, minha classe cliente vai poder continuar invocando o método setOut() que espera um PrintStream: a minha classe adaptadora é um PrintStream! O código completo da classe Executavel.java seria:
import java.io.FileNotFoundException;
import java.io.PrintStream;

public class Executavel {
public static void main(String[] args) throws
FileNotFoundException {
PrintaRelatorio printa = new PrintaRelatorio();
printa.setOut(new AdaptadoraParaPainel());
printa.imprime("texto");
}
}
Pronto! A informação impressa em uma tela e não mais no console, como queria o Cliente, e a classe PrintaRelatorio permanece intocada.

Mas eu ainda tenho algo importante a dizer. Na situação apresentada aqui, o padrão Adapter precisou ser utilizado, de fato. Porém, é preciso notar que: um bom projetista, um arquiteto competente, não teria deixado isto acontecer! A forma como foi construída a classe PrintaRelatorio deixou o mecanismo amarrado ao PrintStream! Esta foi uma péssima decisão! Um bom arquiteto teria notado que:

"Peraí! O Cliente está me pedindo um objeto para imprimir no console. Mas vai que amanhã ele mude de idéia e queira imprimir numa tela? Ou numa página html? Ou direto na impressora? Ou enviar por e-mail? Afinal de contas, um requisito do Cliente foi feito para ser alterado, diversas vezes, durante o projeto..."

Um bom arquiteto teria construído sua classe aplicando o Padrão Bridge. Diz o GoF sobre o Bridge: "desacopla uma abstração da sua implementação, de modo que as duas possam variar independentemente". Se o Padrão Bridge tivesse sido aplicado, não teríamos a necessidade de adaptar mais tarde.

O livro do GoF, na página 160, diz que:
"O padrão Adapter é orientado para fazer com que classes não-relacionadas trabalhem em conjunto. Ele é normalmente aplicado a sistemas que já foram projetados. Por outro lado, Bridge é usado em um projeto, desde o início, para permitir que abstrações e implementações possam variar independentemente."

Mas o Padrão Bridge é assunto para outro post. Não deixe de me enviar uma mensagem em caso de críticas, sugestões ou dúvidas. Obrigado e até a próxima.

26 de maio de 2010

Decorator

Segundo o GoF, "os Decorators fornecem uma alternativa flexível a subclasses para extensão da funcionalidade".

A palavra importante aí é "extensão". Ao se criar um objeto decorador, ele não deverá ter conhecimento da classe que ele vai decorar. Um decorador "barra de rolagem" só deverá saber que ele vai extender a funcionalidade, acrescentando o mecanismo da barra de rolagem. Será utilizado num campo texto? Será utilizado num treeview? Bom, ele não precisa (nem pode) saber!

Por quê eu digo que "nem pode" saber? Porque se ele tiver um conhecimento, uma referência à classe que ele vai decorar, ele vai ficar amarrado a esta classe! Caso você precise agora decorar uma tabela com barra de rolagem, a classe decoradora terá que ser alterada também, para incluir uma nova referência ao objeto tabela. E isso é ruim, muito ruim! Todo novo objeto a ser decorado com barra de rolagem demandará manutenção no código da classe decoradora! Além do mais, isso fere um dos princípios mais básicos das boas práticas da Orientação a Objetos: o Acoplamento, isto é, o grau em que uma classe conhece outra classe. Devemos buscar sempre o
Baixo Acoplamento.

Logo, o objeto decorador "barra de rolagem" só precisa saber que ele vai adicionar este mecanismo ao objeto. Precisa de barra de rolagem num campo texto? Basta fazer campo.add(barraDeRolagem); agora eu preciso em uma tabela... Ok, basta fazer tabela.add(barraDeRolagem); o decorador sofreu alguma alteração no código? Absolutamente
nenhuma!

Utilizando o exemplo do objeto Pizza que deverá ser decorado com Condimentos, obteremos o seguinte: tanto Pizza quanto Condimentos serão produtos alimentícios, dos quais eu vou querer saber o preço e descrição. Teremos:

public interface ProdutoAlimenticio {
public double getCusto();
public String getDescricao();
}
O meu Decorator também será do gênero produto alimentício (queijo, orégano, etc), logo, vai implementar a interface acima. Se eu pedir a descrição dos condimentos da pizza, precisarei imprimir "Queijo, Orégano, Calabreza, etc", ou seja, sobrescrevo o getDescricao() para concatenar com vírgulas. Logo, teremos:
public abstract class CondimentoDecorator
implements ProdutoAlimenticio {

public abstract String getDescricaoDoComplemento();
public String getDescricao() {
return ", " + getDescricaoDoComplemento();
}
}
A classe Pizza deveria implementar a interface ProdutoAlimenticio, porém, vou adicionar uma camada intermediária, entre Pizza e ProdutoAlimenticio, com o intuito de flexibilizar a adição de novos pratos além de Pizza, como por exemplo HotDog. Vou chamar esta classe de Refeição, e veremos na prática como isto vai facilitar o reaproveitamento de código, o que é, aliás, uma das grandes vantagens da Orientação a Objetos.

Portanto, a classe Refeição deverá implementar a interface ProdutoAlimenticio, mas ainda será uma classe abstrata, pois ela ainda não sabe informar o custo da refeição - getCusto() - o que vai ser responsabilidade de suas subclasses, até porque este custo vai ser um para Pizza e outro para HotDog, etc.

Note que na classe Refeição não há qualquer referência a decoradores ou qualquer outro objeto que não interessa a ela. Tudo o que a classe Refeição sabe é que ela é um Produto Alimentício, que refeições têm um custo e uma descrição, e que refeições podem adicionar complementos - que, por sua vez, também serão Produtos Alimentícios. Logo, a classe Refeicao.java fica assim:
public abstract class Refeicao
implements ProdutoAlimenticio{

private String descricao;
private double custo;
protected abstract double getPrecoDaRefeicao();

// Construtor da classe com String como parâmetro
protected Refeicao(String descricao){
this.descricao = descricao;
this.custo = getPrecoDaRefeicao();
}
public double getCusto() {
return custo;
}
public String getDescricao() {
return descricao;
}
public void add(ProdutoAlimenticio complemento) {
custo += complemento.getCusto();
descricao += complemento.getDescricao();
}
}
O que o método add acima faz, é simplesmente incrementar o custo da própria refeição com o custo de seu complemento, e da mesma forma, concatenar sua descrição com a descrição do complemento. Note que este método recebe um ProdutoAlimenticio como parâmetro; é tudo o que a refeição precisa saber - que ela pode adicionar complementos alimentícios. E só!

Agora vamos implementar uma classe concreta de Decorator, por exemplo, Queijo. Você concorda comigo que Queijo poderá decorar tanto Pizzas quanto HotDogs? E que poderá decorar pratos que ainda nem foram criados? Que, se amanhã o cliente inventar um novo prato, e precisar decorá-lo com Queijo, este decorador deverá ser capaz de atendê-lo? Ou seja, está bastante evidente que na classe Queijo NÃO poderá haver referências a pratos, nem a pizzas, nem a refeições. Até porque, na classe CondimentoDecorator.java acima, a regra de concatenar sua descrição com vírgula já foi implementada. Logo, para a classe Queijo.java sobrou apenas informar o seu custo e sua descrição - responsabilidades pertinentes apenas à classe Queijo e à mais ninguém! Portanto, Queijo.java fica apenas assim:
public class Queijo extends CondimentoDecorator {
public double getCusto() {
return 5;
}
public String getDescricaoDoComplemento() {
return "Queijo";
}
}
Ou seja, a classe Queijo é um CondimentoDecorator; e Queijo informa seu custo e sua descrição. Você sequer precisa descobrir quais métodos implementar na classe Queijo! Como Queijo extends CondimentoDecorator, a própria compilação te informa que você precisa implementar o método abstrato getDescricaoDoComplemento(); e que você precisa implementar o método abstrato getCusto() , uma vez que CondimentoDecorator não o implementou de ProdutoAlimenticio. Isto é, não tem como errar! É tão fácil adicionar novos condimentos que iremos adicionar mais um: Calabreza.java :
public class Calabreza extends CondimentoDecorator {
public double getCusto() {
return 7;
}
public String getDescricaoDoComplemento() {
return "Calabreza";
}
}
Agora vamos implementar a classe Pizza. Adivinha o que sobrou para ser implementado em Pizza? Exato! Apenas a responsabilidade do que é de Pizza, ou seja, assim como Queijo e Calabreza, a classe Pizza só precisa informar seu custo e sua descrição! Aqui eu vou delegar ao construtor da classe Pizza a atribuição da descrição, certo? Só para permitir ao Cliente fazer: Pizza marguerita = new Pizza("Marguerita") ou Pizza simples = new Pizza("Mussarela"), etc. Logo, a classe Pizza.java fica apenas assim:
public class Pizza extends Refeicao {
//Construtor atribui a descrição
public Pizza(String descricao) {
super(descricao);
}
public double getPrecoDaRefeicao() {
return 10;
}
}
Da mesma forma, Pizza é uma Refeição. Aqui também a compilação te informa o quê é preciso implementar. Caso você se esqueça de algo (ou algum colega novato na equipe...) será possível identificar o problema em tempo de compilação - o que torna o nosso código muito mais resistente a falhas! Importante perceber também que Pizza.java não tem referência a nenhum decorador. Claro! Nem poderia! Iria ter referência a qual decorador? Queijo? Aqui, da forma como está feito, caso o Cliente crie um novo decorador (orégano, por exemplo), tudo o que eu preciso fazer é criar uma nova classe Oregano.java. Só isso! A classe Pizza.java (nem qualquer outra) não vai sofrer nenhuma alteração.

Bom, vamos testar o projeto numa classe Executavel.java, que contenha um método main():
public class Executavel {
public static void main(String[] args) {
Pizza p = new Pizza("Pizza Completa");
p.add(new Queijo());
p.add(new Calabreza());
System.out.println("Descricao: "+p.getDescricao());
System.out.println("Total: R$ "+p.getCusto());
}
}
Pronto! Tudo o que eu preciso fazer para decorar o objeto Pizza com um novo comportamento é passar o Decorator como parâmetro ao método add(). Note que, como este método precisa de um ProdutoAlimenticio, e Pizza é um ProdutoAlimenticio, eu poderia fazer:

Pizza pizza = new Pizza("Pizza Dupla");
pizza.add(new Pizza(""));

Como exercício, fica pra você a tarefa de criar um novo prato HotDog.java e um novo decorador para o complemento Oregano.java, e acrescentar orégano à Pizza já existente, e também reaproveitar Queijo e Calabreza para adicionar ao HotDog. (Abaixo está a solução)
Tudo se resume a isso:

public class HotDog extends Refeicao {
public HotDog(String descricao) {
super(descricao);
}
public double getPrecoDaRefeicao() {
return 9;
}
}
public class Oregano extends CondimentoDecorator {
public double getCusto() {
return 2;
}
public String getDescricaoDoComplemento() {
return "Orégano";
}
}
public class Executavel {
public static void main(String[] args) {
Pizza p = new Pizza("Pizza");
p.add(new Queijo());
p.add(new Calabreza());
p.add(new Oregano());

HotDog dog = new HotDog("Hot Dog");
dog.add(new Queijo());
dog.add(new Calabreza());

System.out.println("Descricao: "+p.getDescricao());
System.out.println("Total: R$ "+p.getCusto());

System.out.println("Descricao: "+dog.getDescricao());
System.out.println("Total: R$ "+dog.getCusto());
}
}
Bem, espero que tenham gostado! Qualquer dúvida deixe uma mensagem que terei o maior prazer em respondê-la! Também escrevam para enviar sugestões. Pretendo colocar exemplos bem explicativos de todos os 23 padrões do GoF, que é um assunto obrigatório para quem quiser programar orientado a objetos, e onde ainda vejo muita deficiência por aí... Abraços.