Translate

sábado, 23 de junho de 2012

Java - Clonagem de objetos

Durante sua vida um programador pode lidar com a necessidade de manter um objeto matriz e reproduzir cópias deste objeto para fins de alterar estas cópias sem perder a originalidade da matriz.

Posso descrever uma situação onde tive uma rotina de envio de mensagem para um determinado host, mas esta rotina alterava propriedades da minha mensagem original porque seu comportamento normal era este. Foi desenhado para envio de mensagem sempre para um destinatário. Efeito:

  d - Destino da mensagem (host).
  m.o. - Mensagem original.
  m.a. - Mensagem alterada.
  send_to_some (d, m.o. -> m.a.) - Função de envio para d onde m.o. passa a ser m.a. ao final do processamento.

Acrescentei ao sistema uma função a mais para enviar a mensagem destinada para mais de um host através de clonagem para preservar a original. Efeito:

  m.o. - Mensagem original.
  m.c. - Mensagem clonada.
  m.a. - Mensagem alterada.
  send_to_all (m.o. -> m.a.) - Função de envio para todos m.c. passa a ser m.a. ao final do processamento, mas m.o. é preservado.

Após implementar send_to_all, simplismente esta função passou a varrer a lista ativa de conexões de hosts e para cada iteração clona a mensagem original e passa o clone como parâmetro de send_to_some.

Vamos para a prática!

WTByteArrayInputStream

Esta classe representa um mecanismo para transformar uma série de bytes em um objeto. Será utilizada no processo de clonagem.

import java.io.InputStream;

public class WTByteArrayInputStream extends InputStream {

    protected byte[] buf = null;

    protected int count = 0;

    protected int pos = 0;

    public WTByteArrayInputStream(byte[] buf, int count) {
        this.buf = buf;
        this.count = count;
    }

    public final int available() {
        return count - pos;
    }

    public final int read() {
        return (pos < count) ? (buf[pos++] & 0xff) : -1;
    }

    public final int read(byte[] b, int off, int len) {
        if (pos >= count)
            return -1;

        if ((pos + len) > count)
            len = (count - pos);

        System.arraycopy(buf, pos, b, off, len);
        pos += len;
        return len;
    }

    public final long skip(long n) {
        if ((pos + n) > count)
            n = count - pos;
        if (n < 0)
            return 0;
        pos += n;
        return n;
    }

}

WTByteArrayOutputStream

Esta classe representa um mecanismo para transformar um objeto em uma série de bytes e oferece uma função para obter uma conexão com os bytes do objeto.

import java.io.InputStream;
import java.io.OutputStream;

public class WTByteArrayOutputStream extends OutputStream {

    protected byte[] buf = null;
    protected int size = 0;

    public WTByteArrayOutputStream() {
        this(5 * 1024);
    }

    public WTByteArrayOutputStream(int initSize) {
        this.size = 0;
        this.buf = new byte[initSize];
    }

    private void verifyBufferSize(int sz) {
        if (sz > buf.length) {
            byte[] old = buf;
            buf = new byte[Math.max(sz, 2 * buf.length )];
            System.arraycopy(old, 0, buf, 0, old.length);
            old = null;
        }
    }

    public int getSize() {
        return size;
    }

    public byte[] getByteArray() {
        return buf;
    }

    public final void write(byte b[]) {
        verifyBufferSize(size + b.length);
        System.arraycopy(b, 0, buf, size, b.length);
        size += b.length;
    }

    public final void write(byte b[], int off, int len) {
        verifyBufferSize(size + len);
        System.arraycopy(b, off, buf, size, len);
        size += len;
    }

    public final void write(int b) {
        verifyBufferSize(size + 1);
        buf[size++] = (byte) b;
    }

    public void reset() {
        size = 0;
    }

    public InputStream getInputStream() {
        return new WTByteArrayInputStream(buf, size);
    }

}

WTCloneObject

Esta classe é a própria rotina de clonagem.

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class WTCloneObject {

    public static Object copy(Object orig) {

        Object obj = null;

        try {

            WTByteArrayOutputStream fbos = new WTByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(fbos);

            out.writeObject(orig);
            out.flush();
            out.close();

            ObjectInputStream in = new ObjectInputStream(fbos.getInputStream());
            obj = in.readObject();

        }
        catch(IOException e) {
            e.printStackTrace();
        }
        catch(ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
        return obj;
    }

}

DadosPessoais

Esta classe é um bean muito simples que auxiliará apenas o teste. Como será o tipo do objeto para clonar em nosso teste, esta deve implementar a interface Serializable. Observe a palavra-chave transient no parâmetro senha_Bancaria, pois indica que o atributo não será gravado quando o objeto de DadosPessoais for serializado em bytes. Esta mesma técnica pode ser utilizada se um dos atributos for tão complexo ou fechado que não te permite fazer suas classes ou sub-classes implementarem a interface Serializable.

import java.io.Serializable;
 

class DadosPessoais implements Serializable {

    private String nome;
    private int idade;
    private String telefone;
    private String sexo;
    private transient String senha_Bancaria;

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public int getIdade() {
        return idade;
    }

    public void setIdade(int idade) {
        this.idade = idade;
    }

    public String getTelefone() {
        return telefone;
    }

    public void setTelefone(String telefone) {
        this.telefone = telefone;
    }

    public String getSexo() {
        return sexo;
    }

    public void setSexo(String sexo) {
        this.sexo = sexo;
    }

    public String getSenha_Bancaria() {
        return senha_Bancaria;
    }

    public void setSenha_Bancaria(String senha_Bancaria) {
        this.senha_Bancaria = senha_Bancaria;
    }

    @Override
    public String toString() {
        return "DadosPessoais [nome=" + nome + ", idade=" + idade
                + ", telefone=" + telefone + ", sexo=" + sexo
                + ", senha_Bancaria=" + senha_Bancaria + "]";
    }

}

Testando

Crie uma classe de teste com um método main para testar a clonagem:

        DadosPessoais matriz = new DadosPessoais();

        matriz.setNome("Jose");
        matriz.setIdade(30);
        matriz.setTelefone("+55 11 88888888");
        matriz.setSexo("M");
        matriz.setSenha_Bancaria("123#%!");

        System.out.println("Esta é a matriz");
        System.out.println(matriz.toString());
        System.out.println("");

        for (int i = 0; i < 10; i++) {
           
            DadosPessoais clone = (DadosPessoais) WTCloneObject.copy(matriz);

            System.out.println("Este é o clone id = " + i);
            System.out.println(clone.toString());
            System.out.println("");
           
        }

Fique atento, a senha não foi clonada em nenhum momento porque foi marcada como transient na classe DadosPessoais.

Até a próxima.

Nenhum comentário:

Postar um comentário