Padrões de Projeto
Prototype
Fábio Gondim
fmgondim@terra.com.br
Intenção:
Especificar os tipos de objetos a serem criados usando uma instância protótipo e criar novos objetos pela cópia deste protótipo.
2
Considerações
:• A intenção do padrão é a passagem de um objeto que será utilizado como um gabarito para a criação de um novo objeto que contenha as mesmas características do anterior.
3
• Não sendo os detalhes de implementação do objeto plenamente conhecidos não é possível criar uma nova instância da mesma classe concreta e copiar todos os seus dados. • Ainda que a interface do objeto seja
conhecida, alguns destes dados podem ser privados e não dispor de métodos de acesso.
4
• A idéia é, então, pedir ao próprio objeto, seguindo-se o princípio do especialista*, que forneça uma cópia de si mesmo (clone) quando for solicitado.
*Especialista na Informação ou Especialista
Atribuir uma responsabilidade ao especialista na informação: a classe que tem a informação necessária para satisfazer a responsabilidade.
5
Estrutura
Participantes:
•Prototype: Declara uma interface para clonar a si mesmo.
•ConcretePrototype: Implementa uma
operação para clonar a si mesmo.
•Client: Cria um novo objeto solicitando a um protótipo que clone a si mesmo.
Colaborações:
• Um cliente solicita a um protótipo que clone a
si mesmo. 7
O padrão Prototype
na linguagem Java
8
A interface Cloneable
Java fornece uma interface simples, chamada Cloneable, que provê uma implementação do padrão Prototype. Se a classe que contiver o método clone não implementar a interface Cloneable será lançada uma exceção
CloneNotSupportedException. Por convenção, classes que implementam esta interface devem sobrescrever o método protegido, clone(), herdado da classe Object para torná-lo público conforme explicamos a seguir.
9
A classe Object define o método clone da seguinte forma:
protectedObjectclone() throws CloneNotSupportedException Visto que a implementação de clone(), definida em java.lang.object, é protegida (protected), é necessário sobrescrever o método clone para aumentar a sua
visibilidadepara publice poder realizar uma chamada a super.clone().
publicclass CopyMe implements Cloneable {
public Object clone() throws CloneNotSupportedException { return super.clone();
}
} 10
Uma outra alternativa é declarar o tipo de retorno como sendo da própria classe do objeto que está sendo clonado para que não seja necessário realizar a coerção de tipos (casting) na chamada ao método.
public classCopyMeimplements Cloneable {
publicCopyMeclone() throws CloneNotSupportedException { return(CopyMe)super.clone();
} }
11
• O método Object.clone ajuda você a escrever métodos clone para as suas próprias classes. • Um método clone retorna um novo objeto cujo
estado inicial é um cópia do estado atual do objeto sobre o qual clone foi invocado. • Alterações subseqüentes no novo objeto
clonado não devem afetar o estado do objeto original (ver exceções adiante).
Resumindo:
Object.cloneverifica se o objeto sobre o qual foi invocado implementa a interface Cloneablee
lança CloneNotSupportedExceptionse ele não o
faz. Caso contrário, Object.clonecria um novo objeto exatamente do mesmo tipo do objeto original sobre o qual clone foi invocado e inicializa os campos do novo objeto clonado para terem os mesmos valores dos campos do objeto original. Quando Object.clonetermina, ele retorna uma referência do novo objeto.
13
Qual a relação entre os atributos do objeto original e da sua cópia após a chamada a Object.clone?
Tipos primitivos (boolean, char, byte, short, int, long, float e double): é feita uma cópia do atributo com endereços de memória
diferentes, de forma que alterações
subseqüentes nos valores de um objeto não refletirão no outro (os atributos são
independentes).
14
Strings: como as Strings são imutáveisa alteração em um atributo deste tipo em um objeto clonado não refletirá em seu original. Veja a explicação nos slides seguintes.
15
Strings: Strings são objetos especiais que possuem particularidades em relação aos demais objetos. Uma dessas particularidades é que Strings são
imutáveis. Toda vez que uma String é criada , a JVM a coloca em uma parte especial da memória chamada String Pool. Quando já existe uma String com o mesmo valor a JVM não cria uma duplicata apenas faz referência a entrada já existente.
16
Quando é feita qualquer alteração no valor atribuído a uma String uma nova String é criada sem que a antiga seja eliminada da String Pool. E infelizmente o Garbage Colector (Coletor de Lixo) não atua nesta área. Estes valores ficam lá enquanto a aplicação estiver ativa podendo ser, eventualmente, reaproveitados quando houver uma atribuição de um valor já existente.
17
// Após a execução deste trecho de código // teremos 10 (dez) objetos do tipo String // na String Pool e não apenas a String // de valor “0123456789” !!!
String s = “0”;
for (int i = 1; i < 10; i++) { s = s + i;
}
// Para evitar isto utilize StringBuffer ou // ou preferencialmente StringBuilder (Java 5) 18
Wrappers (Boolean, Character, Byte, Short, Integer, Long, Float e Double): da mesma forma que as Strings, Wrappers são imutáveis e, portanto, a alteração em um atributo deste tipo em um objeto clonado não refletirá em seu original.
19
Objetos de tipos definidos no programa ou pela linguagem (Vector, Array, ArrayList, etc.)
excetuando-se os anteriormente descritos (Strings e Wrappers): os atributos destes tipos farão referência aos mesmos objetos, tanto na cópia como no original. Se por exemplo, um objeto clonado possuir uma referência para um Array, o seu clone irá se referir ao mesmo Array e, desta forma, qualquer alteração, em qualquer um dos dois objetos (original e cópia) estará sendo feita sobre a mesma coleção e refletirá nos dois
objetos. 20
Clonagem Superficial (Cópia por Referência) versus
Clonagem Profunda (Cópia por Replicação) A implantação default de clone providencia o que é conhecido como clonagem ou cópia superficial(Shallow Copy) – ele simplesmente realiza um cópia campo a campo. Uma clonagem profunda(Deep Copy)deve clonar cada objeto referido por um campo e cada entrada em uma coleção. Isto se aplica recursivamente, e assim a clonagem profunda de um objeto deve clonar todos os objetos alcançáveis por este objeto. Em geral, clone é sobrescrito para realizar uma clonagem profunda sempre que uma clonagem superficial não é apropriada (ex: a clonagem de um objeto do tipo Carro que contém quatro objetos do tipo Roda – o conjunto de rodas certamente não deve ser compartilhado pelo carro original e
seus clones). 21
Referências Circulares A parte mais difícil do padrão Prototype é a implementação correta da operação Clone. Ela é particularmente difícil quando a estrutura de objetos contêm referências circulares [GoF].
public class Pai { Filho filho // ... }
public class Filho { Pai pai // ... }
Pai referencia Filho
Filho referencia Pai
22
public class Ovelha implements Cloneable {
private String cor; private Double h; private Ovelha mae;
public Ovelha(String cor, double h) { this.cor = cor;
this.h = h; }
public String getCor() { return cor; }
public void setCor(String cor) { this.cor = cor;
}
// Continua ...
23
public Double getH() { return h; }
public void setH(Double h) { this.h = h;
}
public Ovelha getMae() { return mae; }
public void setMae(Ovelha mae) { this.mae = mae;
}
public Ovelha clone() throws CloneNotSupportedException {
return (Ovelha) super.clone();
}
public class ClonaOvelha {
public static void main(String[] args) {
Ovelha ovelhaMae = new Ovelha("preta", new Double(0.55)); Ovelha ovelha = new Ovelha("cinza", new Double(0.6)); ovelha.setMae(ovelhaMae);
try {
Ovelha dolly = ovelha.clone(); System.out.println(dolly.getCor()); System.out.println(dolly.getH()); System.out.println(dolly.getMae().getCor()); dolly.getMae().setCor("grisalha"); System.out.println(dolly.getMae().getCor()); System.out.println(ovelha.getMae().getCor()); dolly.setCor("amarela"); System.out.println(dolly.getCor()); System.out.println(ovelha.getCor()); dolly.setH(new Double(0.8));
System.out.println(dolly.getH()); System.out.println(ovelha.getH());
// Continua ... 25
} catch (CloneNotSupportedException e) {
System.err.println("Não foi possível clonar o objeto"); } // final do try/catch
} // final do main
} // final da classe Saída do Programa:
cinza 0.6 preta grisalha grisalha amarela cinza 0.8 0.6 26
Bibliografia
• ARNOLD, Ken, GOSLING, James, HOLMES, David. A Linguagem de Programação Java, 4ª. Ed. Bookman. 2007;
• BATES, Bert, SIERRA Kathy. Head First Java, 2ª. Ed. O’Reilly, 2005;
• LARMAN, Craig. Utilizando UML e Padrões, 2ª. Ed. Bookman, 2004;
• GAMMA, E., HELM, R., JOHNSON, R. e VLISSIDES, J. Padrões de Projeto –
Soluções reutilizáveis de software orientado a objetos. Bookman. 1995.