Design Pattern: Implementando o Decorator

Posted by Alberto Leal on June 23rd, 2009

Photo: http://static.howstuffworks.com/gif/teen-bedroom-decorating-ideas-2.jpg

O último livro que terminei de ler era sobre Design Pattern. Então, resolvi fazer alguns posts contendo algumas implementações. O padrão de projeto da vez é o Decorator.

——–

O poder da composição

Têm quem ame, e têm quem odeie herança. Existem casos que não há como fugir da herança, mas, sempre que possível, prefira composição a herança. Quando utilizamos herança em uma parte da nossa aplicação, e alguns comportamentos são herdados, estes  são definidos estaticamente em tempo de compilação. Por outro lado, se você estender o comportamento de um objeto por meio de composição, você consegue fazer isso dinamicamente, ou seja, consegue compôr os objetos de forma dinâmica. Com isso, se torna fácil adicionar novas funcionalidades escrevendo um código novo ao invés de alterar o código existente. Isso é bacana, pois o comportamento anterior não é modificado, assim as chances de introdução de erros no código que já estava funcionando é menor. Se algo der errado, basta remover a nova classe que foi criada, por exemplo. Resumindo, mantenha as classes abertas para extensão e fechadas para alteração.

O padrão de projeto decorator anexa novas responsabilidades em um objeto dinamicamente, utilizando  do poder da composição.

Vamos ver como isso funciona na prática. O exemplo que lhe será apresentado é a construção de uma pizza. No momento do pedido, o cliente poderá pedir ao Pizzaiolo que adicione  novos ingredientes na pizza, tais como: orégano e tomate. Exemplo simples e direto, apenas para ilustrar.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package br.eti.albertoleal.decorator;
 
public abstract class Pizza {
 
private String description = "Unknown Pizza";
 
public void setDescription(String desc) {
description = desc;
}
 
public String getDescription(){
return description;
}
 
public abstract double cost();
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package br.eti.albertoleal.decorator;
 
public class Cheese extends Pizza {
 
	public Cheese(){
		setDescription("Cheese Pizza")
	}
 
	@Override
	public double cost() {
		return 19.90;
	}
 
}

As classes decorator devem ser espelhos das classes que elas vão decorar. Vamos utilizar da herança em nossos objetos do tipo CondimentDecorator. O motivo de se usar herança na classe Decorator é  pelo simples fato de se ter o mesmo tipo dos objetos que vão ser decorados. O uso de herança é justamente para atingir  essa correspondência de tipo.

1
2
3
4
5
package br.eti.albertoleal.decorator;
 
public abstract class CondimentDecorator extends Pizza {
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package br.eti.albertoleal.decorator;
 
public class Oregano extends CondimentDecorator {
	Pizza pizza;
 
	public Oregano(Pizza pz) {
		pizza = pz;
	}
 
	@Override
	public String getDescription() {
		return pizza.getDescription() + ", Oregano";
	}
 
	@Override
	public double cost() {
		return .50 + pizza.cost();
	}
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package br.eti.albertoleal.decorator;
 
public class Tomato extends CondimentDecorator {
	Pizza pizza;
 
	public Tomato(Pizza pz){
		pizza = pz;
	}
 
	@Override
	public String getDescription() {
		return pizza.getDescription() + ", Tomato ";
	}
 
	@Override
	public double cost() {
		return .10 + pizza.cost();
	}
 
}

Se você estava atento reparou que a cada condimento que é adicionado na pizza um valor correspondente ao condimento é somado ao valor da pizza. Essa é a idéia do decorator. Estou decorando um objeto pizza com diversos outros componentes. Um outro exemplo, bastante comum de se encontrar pela internet, são as janelas gráficas. O ato de decorar uma janela gráfica com diversos componentes de apresentação, tais como: barra de rolagem, campos de texto e por aí vai.

Chegou a hora de testarmos o nosso código, criar uma pizza e adicionar alguns condimentos a ela e constatar se a nossa “decoração” funciona:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package br.eti.albertoleal.decorator;
 
public class Pizzaiolo {
 
	public static void main(String[] args) {
 
		Pizza pz = new Cheese();
 
		pz = new Tomato(pz);
		pz = new Tomato(pz);
		pz = new Oregano(pz);
		pz = new Tomato(pz);
		pz = new Oregano(pz);
		pz = new Tomato(pz);
 
		System.out.println(pz.getDescription());		
 
	}
 
}

Concluindo:

  • A herança é uma maneira de estender comportamentos, mas não é a melhor maneira de obter flexibilidade;
  • Composição e delegação são boas maneiras de se adicionar funcionalidades em tempo de execução;
  • A utilização de um componente decorator é simplesmente decorar um objeto, e o objeto decorado não precisa conhecer os decoradores;
  • É muito importante que os objetos decoradores sejam espelhos do objeto que vão decorar, justamente para ter uma correspondência de tipo.

Membro Privado é herdado, mas não é visível!

Posted by Alberto on March 5th, 2008

[EDITADO]
Após alguns comentários, resolvi buscar outras fontes de leitura. E estou convencido de que errei. Membro privado não herda. :$
[/EDITADO]

Meses atrás houve um grande debate entre alguns amigos sobre a seguinte questão:Afinal, métodos e propriedades privados em java, são herdados pelas classes-filhas?

Tem gente que acredita que herda, tem gente que acredita que não herda e tem gente que não acha nada!
Estou no grupo que acredita que herda!

Qualquer membro que tenha sua visibilidade marcada como private, é herdada pelas classes-filhas.Porém, somente a classe onde o membro foi declarado terá acesso (devido ao tipo visibilidade).

Este é um bom assunto para juntar um grupo de amigos e discutir. Certamente iniciará uma Flame War :X

Vou tentar convencer vocês a acreditarem que herda .

Retirado do wikipédia, de forma resumida, o conceito de herança é:

“A Herança é um princípio da Programação Orientada a Objetos que permite que as classes compartilhem atributos e operações baseados em um relacionamento, geralmente generalização. A herança permite a criação de subclasses que herdam atributos e operações (ou Métodos) da classe pai (super-classe ou classe base)

Do meu ponto de vista, o que ocorre, em Java especificamente, é o seguinte:

Herança é uma coisa, e escopo de um membro é outra. Quando tornamos um membro “private”, estamos falando que o seu escopo é apenas dentro da própria classe. O que é totalmente diferente de: Os objetos das classes-filhas não herdaram este(s) membro(s).

O grupo que acha que não herda alega que as propriedades não estão presentes na classe filha, não são visíveis.

Durante a “discussão” herda, não herda, nos deparamos com opiniões de outras pessoas. Vimos que toda a estrutura das classes são alocadas em memória e prontas para serem utilizadas pelos mecanismos internos da classe. Ou seja, os membros estão lá. É fato que não é possível acessar de forma direta os membros privados da superclasse, a não ser que seja via reflexão. O que foge da discussão aqui.

Uma outra coisa que é legal comentar é que, não existe polimorfismo com membros privados. Caso exista um membro de mesmo nome nas classes-filhas, este será apenas um novo membro com escopo diferente.

Este, talvez, seja o motivo da GRANDE confussão, já que quando estamos implementando a classe-filha temos a sensação de que os membros privados não são herdados.

Engenharia de Software Magazine

Posted by Alberto on March 4th, 2008

Para quem não conhece, a Devmedia é uma das editoras mais importantes na área de análise/desenvolvimento atuante no Brasil. Particularmente, posso falar sobre 3(revistas) revistas: SQL Magazine ,Web Mobile e Java Magazine (atualmente só assino Java Magazine e Web Mobile). São revistas excepcionais, com conteúdo de primeira! Recentemente foi lançada uma nova revista - digital, sobre Engenharia de Software. Ainda não tive a oportunidade de ler, pois acabei de receber um email comunicando o lançamento, mas já dei uma olhada nos artigos e parecem interessantes.

Engenharia de Software é a aplicação de abordagens sistemáticas, disciplinadas e quantificáveis no desenvolvimento e manutenção de software. Desta forma, se preocupa em como realizar as diversas atividades envolvidas no processo de desenvolvimento de software de forma que se tenha um produto elaborado com maior qualidade e menor custo.

Fonte: http://www.devmedia.com.br/esmag/

Screencast - Refatoração na Prática

Posted by Alberto on January 14th, 2008

Preparem a pipoca!!

É com muita satisfação que apresento à vocês o primeiro screencast do Blog.

Este é o primeiro de muitos. A idéia é publicar uma série de vídeos com dicas sobre desenvolvimento de software.

Para iniciar a nossa série de screencast, apresento-lhes o primeiro: “Refatoração na Prática”, onde mostro como aplicar técnicas de refactoring com o auxíio de uma IDE.

PS: Não pensava que daria tanto trabalho. Para quem nunca mexeu com edição de vídeo até que está muito bom. Por isso, peço que aliviem o meu lado nos primeiros vídeos.. =D

Espero que gostem…

Link no YouTube: http://www.youtube.com/watch?v=WPF6qriM8wo

Link para Download: http://www.albertoleal.eti.br/Screencast/ScreenCast-Refatoracao_na_Pratica.avi

Testar, pra quê?

Posted by Alberto on January 11th, 2008

Vamos ver uma definição de “teste”.

Dicionário:

“acto ou efeito de verificar ou provar alguma coisa (equipamento, sistema), sob certas condições, para verificar e avaliar a sua qualidade, natureza ou comportamento.

Na nossa área, quando testamos algo ( funcionalmente,unitariamente..) queremos garantir que dada uma entrada o resultado seja uma saída esperada.

Testar um sistema de software não é simplesmente sentar na frente do computador e iniciar um processo exaustivo de entrada de informações e aguardar a sua resposta. Existem diversas maneiras de se testar um software - estrutrados, OO… A melhor forma de se testar uma aplicação é elaborando um conjunto de testes e automatizando-o. Existem enumeras ferramentas que nos auxiliam na criação de testes (jUnit, por exemplo), o que torna mais simples utilizar os testes como um artefato de apoio na construção de sistemas de software.

Até mesmo para elaborar testes, deve haver um planejamento (menor valor absoluto, maior valor absoluto, métricas..). Para isso existem técnicas, como por exemplo:

  • Teste Caixa Branca;
  • Teste Caixa Preta;
  • Teste Funcional;
  • Teste de Confiabilidade;
  • etc…

Teste bem elaborados podem preservar a semântica do programa. Alguns desenvolvedores pregam que os testes devam ser planejados antes mesmo que uma linha de código tenha sido implementada. Dessa forma, o desenvolvedor se questiona sobre a necessidade de se alterar alguma coisa no requisito.

O artefato “TESTES” de um projeto não tem como objetivo único e exclusivamente verificar se uma funcionalidade retorna uma saída esperada. Quando estamos planejando um conjunto sólido de testes, estamos verificando se todos os requisitos foram atendidos, se o objetivo da aplicação está claro para todos os envolvidos, ou seja, além de se testar um software, a organização do projeto estará sendo sempre validada e confirmada.


Copyright © 2007 Alberto Leal. All rights reserved.