Classe Abstrata : Escalabilidade

11 respostas
Cassio_Tessaro

Saudações pessoal.

Estou desenvolvendo um projeto e tenho uma dúvida :

Tenho uma classe que se chama Contatos. Essa classe é abstrata.
Quero utiizar ela como referencia para outras classes, como se fosse um modelo pois tenho vários tipos de contatos : Contatos de Clientes, Contatos de fornecedores e etc. Cada um implementando pequenas modificações. Essa Classe abstrata que serve de modelo esta em um pacote diferente das demais. Como os atributos dela estao declarados como private consigo apenas ter acesso a estes membros atraves dos metodos getters e setters. Essa seria a melhor maneira de implementar esse modelo ? ou teria que declarar seus atributos como protected ?
Ou implementaria de alguma outra maneira ?

Poderiam me dar uma ajudinha ?

Desde ja agradeço a colaboração de todos.

Um grande abraço!

11 Respostas

O

Amigo, tambem estou começando agora, porem estou utilizando o padrao EJB onde eu crio uma Interface (Contato), só com o metedos GET e SET e depois a classe implementadora (ContatoBean) onde eu informo os membros como private e implementos os metodos com this no SET e return no GET. Depois é só usar HERANÇA.

Cassio_Tessaro

Valeu o_0

Uhummmm mas qual a diferença em criar uma classe que seja uma interface e fazer isso direto por uma classe abstrata ??
O grande X da questao é que os atributos estao como private e não conseguirei acessa los mesmo que por herança não importa o tipo de classe que eu fizer isso.
Não é mesmo ?
Sendo assim a pergunta é : Tem algum problema em nao conseguir visualizar os atributos da minha super classe e apenas manipula los atraves dos metodos setters e getters ??

Se tiver problema, nao resolveria apenas colocando os atributos da superclasse como protected (permitindo assim o acesso em heranças ) ?

Cara teria como tu postar um exemplo ai ?

Valeu.

gabrielmskate

Eu posso te dizer que é uma má pratica deixar um atributo visível a outra classe.
Mesmo que não tenha necessidade agora de usar get e set, é bom você se proteger para mudanças futuras.
Uma classe sabe como cuidar dos seus próprios atributos, uma classe só deve se comunicar com outra através de sua interface (método get e set).
Depois da uma lida sobre encapsulamento.
E esse artigo também é muito bom – > http://blog.caelum.com.br/2006/09/14/nao-aprender-oo-getters-e-setters/

ViniGodoy

Que tipo de métodos? Não seria possível declarar uma outra classe e fazer composição?

Quando possível, é sempre melhor usar composição do que herança.

gabrielmskate

Reforçando o que o ViniGodoy falou, tem um artigo da caelum que fala sobre isso -> http://blog.caelum.com.br/2006/10/14/como-nao-aprender-orientacao-a-objetos-heranca/

[]s

Cassio_Tessaro

ViniGodoy:
Que tipo de métodos? Não seria possível declarar uma outra classe e fazer composição?

Quando possível, é sempre melhor usar composição do que herança.

Sim essa classe Contatos… faz parte de uma composição mas nao é essa a questao…
a questao é para escrever menos codigo e o que eu fiz ? juntei todos os atributos que compoem as classes Contatos de todas as classes e coloquei em uma classe abastrata :

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.lojavirtual.www.classes.uteis;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author dakilla
 */
public abstract class Contatos {

    private int idContatos;
    private String email;
    private String telefoneFixo;
    private String telefoneCelular;
    private String codigoAreaCelular;
    private String codigoAreaFixo;

    public Contatos() {
        //Construtor Padrão
    }

    public Contatos(String email, String telefoneFixo, String telefoneCelular) {
        //Construtor que deve ser utilizado :
        setEmail(email);
        setTelefoneCelular(telefoneCelular);
        setTelefoneFixo(telefoneFixo);
    }

    /*
     * Inicio dos Métodos Getters e Setters
     *
     */
    public int getIdContatos() {
        return idContatos;
    }

    public void setIdContatos(int idContatos) {
        this.idContatos = idContatos;
    }

    public String getCodigoAreaCelular() {
        return codigoAreaCelular;
    }

    public boolean setCodigoAreaCelular(String codigoAreaCelular) {
        if (validarCodigoArea(codigoAreaCelular)) {
            this.codigoAreaCelular = codigoAreaCelular;
            return true;
        } else {
            return false;
        }

    }

    public String getCodigoAreaFixo() {
        return codigoAreaFixo;
    }

    public boolean setCodigoAreaFixo(String codigoAreaFixo) {
        if (validarCodigoArea(codigoAreaFixo)) {
            this.codigoAreaFixo = codigoAreaFixo;
            return true;
        } else {
            return false;
        }

    }

    public String getEmail() {
        return email;
    }

    public boolean setEmail(String email) {
        if (validarEmail(email)) {
            this.email = email;
            return true;
        } else {
            return false;
        }

    }

    public String getTelefoneCelular() {
        return telefoneCelular;
    }

    public boolean setTelefoneCelular(String telefoneCelular) {
        if (validarTelefoneCelular(telefoneCelular)) {
            this.telefoneCelular = telefoneCelular;
            return true;
        } else {
            return false;
        }

    }

    public String getTelefoneFixo() {
        return telefoneFixo;
    }

    public boolean setTelefoneFixo(String telefoneFixo) {
        if (validarTelefoneFixo(telefoneFixo)) {
            this.telefoneFixo = telefoneFixo;
            return true;
        } else {
            return false;
        }

    }

    /*
     * Inicio de Métodos de validação
     *
     */
    public boolean validarEmail(String email) {
        if (email.length() < 1) {
            return false;
        } else {

            /* Padrão de email :
             * Pode começar com letras de a até z maiusculo e minusculo
             * Pode começar com numeros 0 ate  9
             * Deve ter apenas um @
             * pode ter um ou mais .
             * pode conter _.-
             * Deve conter de 2 a 4 letras no final
             */
            Pattern padrao = Pattern.compile("[a-zA-Z0-9]+[a-zA-Z0-9_.-]+@{1}[a-zA-Z0-9_.-]*\.+[a-z]{2,4}");
            Matcher valida = padrao.matcher(email);
            if (valida.matches()) {
                return true;
            } else {
                return false;
            }


        }

    }

    public boolean validarTelefoneFixo(String telefone) {
        if (telefone.length() < 7) {
            return false;
        } else {
            /*Padrao Telefone Fixo
             *Deve começar com numeros de 2 ate 6
             * Deve ter numeros de 0 ate 9
             * no máximo 8 caracteres
             */
            Pattern padrao = Pattern.compile("[2-6]{1}[0-9]{7}");
            Matcher valida = padrao.matcher(telefone);
            if (valida.matches()) {
                return true;
            } else {
                return false;
            }
        }
    }

    public boolean validarTelefoneCelular(String telefone) {
        if (telefone.length() < 7) {
            return false;
        } else {
            /*Padrao Telefone Celular
             *Deve começar com numeros de 6 ate 9 
             * Deve ter numeros de 0 ate 9
             * no máximo 8 caracteres
             */

            Pattern padrao = Pattern.compile("[6-9]{1}[0-9]{7}");
            Matcher valida = padrao.matcher(telefone);
            if (valida.matches()) {
                return true;
            } else {
                return false;
            }
        }
    }

    public boolean validarCodigoArea(String codigo) {
        /* Padrao Codigo de area
         * Doi digitos
         * de 1 ate 9
         * Exmplo 11,12,56,63,99
         */
        Pattern padrao = Pattern.compile("[1-9]{2}");
        Matcher valida = padrao.matcher(codigo);
        if (valida.matches()) {
            return true;
        } else {
            return false;
        }
    }
}

Então essa classe abstrata fica como um modelo para todas as outras classes que precisam de um contato. Exemplo:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.lojavirtual.www.Fornecedores;

import br.com.lojavirtual.www.classes.uteis.Contatos;

/**
 *
 * @author Dakilla
 */
public class ContatosFornecedor extends Contatos {

    public ContatosFornecedor() {
    }

    public ContatosFornecedor(String email, String telefoneFixo, String telefoneCelular, String codigoAreaCelular, String codigoAreaFixo) {
        setEmail(email);
        setTelefoneFixo(telefoneFixo);
        setTelefoneCelular(telefoneCelular);
        setCodigoAreaCelular(codigoAreaCelular);
        setCodigoAreaFixo(codigoAreaFixo);

    }
    
}

Dai vem a duvida que eu ja consegui responder testando os pacotes e modulos que eu havia criado.
Se eu conseguiria acessar os atributos da super classe com os metodos getters em setters dela atraves da classe filho.
E sim isso é possivel.

O que fica ainda de duvida é : Isso é correto ? esse é o padrao ?

Isso me economiza muito codigo.

O artigo citado pelo nosso amigo é muito bom mas nao consegui identificar se ainda assim isso é a maneira mais correta de fazer.
Isso ajuda também na escalabilidade do meu sistema em sí. Ate mesmo para atualizar. Se eu quiser alter a validação de email por exemplo, altero do na classe pai.

Agradeço a todos pela participação.
Quanto mais ajuda melhor.

ViniGodoy

Repare que você tem atributos que andam juntos… Como o telefone e o código de área… Aliás, esses atributos tem até métodos em comum, como a validação se são válidos ou não.

Comece refatorando por aí. Isso vai reduzir muito código.

Crie uma classe telefone, com telefone, código de área e pattern de validação. Você pode definir diferentes patterns num enum, para que o usuário escolha depois.
Isso já deve reduzir muito código.

Cassio_Tessaro

ViniGodoy:
Repare que você tem atributos que andam juntos… Como o telefone e o código de área… Aliás, esses atributos tem até métodos em comum, como a validação se são válidos ou não.

Comece refatorando por aí. Isso vai reduzir muito código.

Crie uma classe telefone, com telefone, código de área e pattern de validação. Você pode definir diferentes patterns num enum, para que o usuário escolha depois.
Isso já deve reduzir muito código.

Cara essa ja é a minha classe rduzida… O scodigos de area podem ser diferentes para celular e fixo… eu tenho amigos que vieram do interior que possuem um codigo de area para cel e outro para fixo… buenas… nao deixei junto pois sei la … me parece mais organizado assim… talvez ocupe um pouco mais de memoria e processamento mas acho que vale apena em termos de custo beneficio…
E tmb posso fazer uma sobre escrita de metodos ja que são publico na validação onde precisar, mas assim ja esta me economizando linhas a grande questao é: esta correto assim ???

valeuzzzz

ViniGodoy

Ok, vamos mostrar como eu faria. Para começar, eu separaria as classes Telefone e Email. Afinal, são dados com regras próprias. O telefone divide-se por tipos, portanto, também criaria um enum para representar os tipos de telefone.

Depois, alteraria a classe de contatos para refletir essas mudanças:

Arquivo Email.java
package br.com.lojavirtual.www.classes.uteis;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class Email {
	private String email;

	public static boolean validar(String email) {
		if (email == null || email.length() < 1) {
			return false;
		}

		/*
		 * Padrão de email : Pode começar com letras de a até z maiusculo e
		 * minusculo Pode começar com numeros 0 ate 9 Deve ter apenas um @ pode
		 * ter um ou mais . pode conter _.- Deve conter de 2 a 4 letras no final
		 */
		Pattern padrao = Pattern
				.compile("[a-zA-Z0-9]+[a-zA-Z0-9_.-]+@{1}[a-zA-Z0-9_.-]*\.+[a-z]{2,4}");
		Matcher valida = padrao.matcher(email);
		return valida.matches();
	}

	public Email(String email) {
		setEmail(email);
	}

	public void setEmail(String email) {
		if (!Email.validar(email)) {
			throw new IllegalArgumentException("E-mail inválido!");
		}
		this.email = email;
	}
	
	public String getEmail() {
		return email;
	}
}

Arquivo Telefone.java

package br.com.lojavirtual.www.classes.uteis;


public final class Telefone {
	private String codigoArea;
	private String telefone;
	private TipoTelefone tipo;

	public Telefone(TipoTelefone tipo, String codigoArea, String telefone) {
		this.tipo = tipo;
		setCodigoArea(codigoArea);
		setTelefone(telefone);
	}

	public void setTelefone(String telefone) {
		if (!tipo.validarTelefone(telefone)) {
			throw new IllegalArgumentException("Telefone inválido!");
		}
		this.telefone = telefone;
	}

	public void setCodigoArea(String codigoArea) {
		if (!tipo.validarCodigoArea(codigoArea)) {
			throw new IllegalArgumentException("Codigo de área inválido!");
		}
		this.codigoArea = codigoArea;
	}

	public String getCodigoArea() {
		return codigoArea;
	}
	
	public String getTelefone() {
		return telefone;
	}
	
	public TipoTelefone getTipo() {
		return tipo;
	}
}

Arquivo TipoTelefone.java

package br.com.lojavirtual.www.classes.uteis;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public enum TipoTelefone {
	
	/**Padrao Telefone Fixo<p> 
	* <ul>
	* <li>Deve começar com numeros de 2 ate 6
	* <li>Deve ter no máximo 8 caracteres
	* </ul> 
	*/  
	FIXO("[1-9][0-9]", "[2-6]{1}[0-9]{7}"),
	
	/** Padrao Telefone Celular.<p>
	 * <ul>
	 * <li>Deve começar com numeros de 6 ate 9
	 *  <li>Deve ter numeros de 0 ate 9
	 *  <li>no máximo 8 caracteres
	 *  </ul> 
	 */  	
	CELULAR("[1-9][0-9]", "[6-9]{1}[0-9]{7}");
	private String patternCodigoArea;	
	private String patternTelefone;

	private TipoTelefone(String patternCodigoArea, String patternTelefone) {
		this.patternCodigoArea = patternCodigoArea;
		this.patternTelefone = patternTelefone;
	}

	public boolean validarTelefone(String telefone) {
		if (telefone.length() < 7) {
			return false;
		}

		Pattern padrao = Pattern.compile(patternTelefone);
		Matcher valida = padrao.matcher(telefone);
		return valida.matches();
	}

	public boolean validarCodigoArea(String codigoArea) {
		Pattern padrao = Pattern.compile(patternCodigoArea);
		Matcher valida = padrao.matcher(codigoArea);
		return valida.matches();
	}
}
Arquivo Contato.java
package br.com.lojavirtual.www.classes.uteis;

public abstract class Contato {
	private int id;
	private Email email;
	private Telefone fixo;
	private Telefone celular;
	
	//Não coloque construtor padrão se vc tem dados obrigatórios, como o id.
	public Contato(int id)
	{
		this.id = id;		
	}
	
	public Contato(int id, Email email, Telefone fixo, Telefone celular)
	{
		this(id);
		
		if (fixo.getTipo() != TipoTelefone.FIXO)
			throw new IllegalArgumentException("Informe um telefone fixo!");
		if (celular.getTipo() != TipoTelefone.CELULAR)
			throw new IllegalArgumentException("Informe um telefone fixo!");		
		this.email = email;
		this.fixo = fixo;
		this.celular = celular;
	}
	
	public int getId() {
		return id;
	}
	
	public Telefone getFixo() {
		return fixo;
	}
	
	public Telefone getCelular() {
		return celular;
	}
	
	public void setFixo(Telefone fixo) {
		if (fixo.getTipo() != TipoTelefone.FIXO)
			throw new IllegalArgumentException("Informe um telefone fixo!");
		
		this.fixo = fixo;
	}
	
	public void setCelular(Telefone celular) {
		if (celular.getTipo() != TipoTelefone.CELULAR)
			throw new IllegalArgumentException("Informe um telefone celular!");		
		
		this.celular = celular;
	}
	
	public void setEmail(Email email)
	{
		this.email = email;
	}
	
	public Email getEmail() {
		return email;
	}	
}

Note que com blocos menores, ficaria muitíssimo fácil incluir um novo tipo de telefone. Bastaria incluir um tipo a mais no enum.
Também seria fácil alterar o contato para ter mais telefones. Ou para que o usuário especifique 2 telefones, de qualquer tipo (para, por exemplo, na falta de um celular, o Contato ter dois fixos, o de casa e o da empresa).

Não fiquei muito convencido de que os métodos de validação de telefone tenham que testar o length(). Talvez fosse melhor deixar esse trabalho para a expressão regular. Note que também reduzi código ao eliminar seus ifs com atributos booleanos (como no caso do matches()). Você testava "se matches() é true, retorna true, senão, se matches() é false, retorna false". Ora, é mais fácil retornar matches de uma vez!

Outra coisa... Também pode-se reduzir código e identação se, sempre que seu if disparar uma exceção ou tiver um "return", você eliminar o else.

Classes menores também se tornam mais coesas e reaproveitaveis. Se no futuro você criar uma classe para empresas, provavelmente poderá reusar os tipos Telefone e Email. Aliás, você pode até mesmo separar esses tipos num outro .jar, e usar em diversos projetos, além do da loja virtual.

A dica é, não tenha medo de criar classes. É muito melhor ter um conjunto de classes pequenas e coesas, do que ter uma hierarquia de uma classe só enorme.

NightDark

ViniGodoy deu uma verdadeira aula^^ gostei :lol:

Cássio Tessaro, te aconselho a ler o livro Java Efetivo do autor Joshua Bloch, tem várias páginas referente a sua dúvida, Estou lendo e gostando bastante :wink:

Cassio_Tessaro
ViniGodoy:
Ok, vamos mostrar como eu faria. Para começar, eu separaria as classes Telefone e Email. Afinal, são dados com regras próprias. O telefone divide-se por tipos, portanto, também criaria um enum para representar os tipos de telefone.

Depois, alteraria a classe de contatos para refletir essas mudanças:

Arquivo Email.java
package br.com.lojavirtual.www.classes.uteis;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class Email {
	private String email;

	public static boolean validar(String email) {
		if (email == null || email.length() < 1) {
			return false;
		}

		/*
		 * Padrão de email : Pode começar com letras de a até z maiusculo e
		 * minusculo Pode começar com numeros 0 ate 9 Deve ter apenas um @ pode
		 * ter um ou mais . pode conter _.- Deve conter de 2 a 4 letras no final
		 */
		Pattern padrao = Pattern
				.compile("[a-zA-Z0-9]+[a-zA-Z0-9_.-]+@{1}[a-zA-Z0-9_.-]*\.+[a-z]{2,4}");
		Matcher valida = padrao.matcher(email);
		return valida.matches();
	}

	public Email(String email) {
		setEmail(email);
	}

	public void setEmail(String email) {
		if (!Email.validar(email)) {
			throw new IllegalArgumentException("E-mail inválido!");
		}
		this.email = email;
	}
	
	public String getEmail() {
		return email;
	}
}

Arquivo Telefone.java

package br.com.lojavirtual.www.classes.uteis;


public final class Telefone {
	private String codigoArea;
	private String telefone;
	private TipoTelefone tipo;

	public Telefone(TipoTelefone tipo, String codigoArea, String telefone) {
		this.tipo = tipo;
		setCodigoArea(codigoArea);
		setTelefone(telefone);
	}

	public void setTelefone(String telefone) {
		if (!tipo.validarTelefone(telefone)) {
			throw new IllegalArgumentException("Telefone inválido!");
		}
		this.telefone = telefone;
	}

	public void setCodigoArea(String codigoArea) {
		if (!tipo.validarCodigoArea(codigoArea)) {
			throw new IllegalArgumentException("Codigo de área inválido!");
		}
		this.codigoArea = codigoArea;
	}

	public String getCodigoArea() {
		return codigoArea;
	}
	
	public String getTelefone() {
		return telefone;
	}
	
	public TipoTelefone getTipo() {
		return tipo;
	}
}

Arquivo TipoTelefone.java

package br.com.lojavirtual.www.classes.uteis;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public enum TipoTelefone {
	
	/**Padrao Telefone Fixo<p> 
	* <ul>
	* <li>Deve começar com numeros de 2 ate 6
	* <li>Deve ter no máximo 8 caracteres
	* </ul> 
	*/  
	FIXO("[1-9][0-9]", "[2-6]{1}[0-9]{7}"),
	
	/** Padrao Telefone Celular.<p>
	 * <ul>
	 * <li>Deve começar com numeros de 6 ate 9
	 *  <li>Deve ter numeros de 0 ate 9
	 *  <li>no máximo 8 caracteres
	 *  </ul> 
	 */  	
	CELULAR("[1-9][0-9]", "[6-9]{1}[0-9]{7}");
	private String patternCodigoArea;	
	private String patternTelefone;

	private TipoTelefone(String patternCodigoArea, String patternTelefone) {
		this.patternCodigoArea = patternCodigoArea;
		this.patternTelefone = patternTelefone;
	}

	public boolean validarTelefone(String telefone) {
		if (telefone.length() < 7) {
			return false;
		}

		Pattern padrao = Pattern.compile(patternTelefone);
		Matcher valida = padrao.matcher(telefone);
		return valida.matches();
	}

	public boolean validarCodigoArea(String codigoArea) {
		Pattern padrao = Pattern.compile(patternCodigoArea);
		Matcher valida = padrao.matcher(codigoArea);
		return valida.matches();
	}
}
Arquivo Contato.java
package br.com.lojavirtual.www.classes.uteis;

public abstract class Contato {
	private int id;
	private Email email;
	private Telefone fixo;
	private Telefone celular;
	
	//Não coloque construtor padrão se vc tem dados obrigatórios, como o id.
	public Contato(int id)
	{
		this.id = id;		
	}
	
	public Contato(int id, Email email, Telefone fixo, Telefone celular)
	{
		this(id);
		
		if (fixo.getTipo() != TipoTelefone.FIXO)
			throw new IllegalArgumentException("Informe um telefone fixo!");
		if (celular.getTipo() != TipoTelefone.CELULAR)
			throw new IllegalArgumentException("Informe um telefone fixo!");		
		this.email = email;
		this.fixo = fixo;
		this.celular = celular;
	}
	
	public int getId() {
		return id;
	}
	
	public Telefone getFixo() {
		return fixo;
	}
	
	public Telefone getCelular() {
		return celular;
	}
	
	public void setFixo(Telefone fixo) {
		if (fixo.getTipo() != TipoTelefone.FIXO)
			throw new IllegalArgumentException("Informe um telefone fixo!");
		
		this.fixo = fixo;
	}
	
	public void setCelular(Telefone celular) {
		if (celular.getTipo() != TipoTelefone.CELULAR)
			throw new IllegalArgumentException("Informe um telefone celular!");		
		
		this.celular = celular;
	}
	
	public void setEmail(Email email)
	{
		this.email = email;
	}
	
	public Email getEmail() {
		return email;
	}	
}

Note que com blocos menores, ficaria muitíssimo fácil incluir um novo tipo de telefone. Bastaria incluir um tipo a mais no enum.
Também seria fácil alterar o contato para ter mais telefones. Ou para que o usuário especifique 2 telefones, de qualquer tipo (para, por exemplo, na falta de um celular, o Contato ter dois fixos, o de casa e o da empresa).

Não fiquei muito convencido de que os métodos de validação de telefone tenham que testar o length(). Talvez fosse melhor deixar esse trabalho para a expressão regular. Note que também reduzi código ao eliminar seus ifs com atributos booleanos (como no caso do matches()). Você testava "se matches() é true, retorna true, senão, se matches() é false, retorna false". Ora, é mais fácil retornar matches de uma vez!

Outra coisa... Também pode-se reduzir código e identação se, sempre que seu if disparar uma exceção ou tiver um "return", você eliminar o else.

Classes menores também se tornam mais coesas e reaproveitaveis. Se no futuro você criar uma classe para empresas, provavelmente poderá reusar os tipos Telefone e Email. Aliás, você pode até mesmo separar esses tipos num outro .jar, e usar em diversos projetos, além do da loja virtual.

A dica é, não tenha medo de criar classes. É muito melhor ter um conjunto de classes pequenas e coesas, do que ter uma hierarquia de uma classe só enorme.

Opaaaaaa desculpa pela demora em responder.... :oops: Obrigado pela aula literamente.... huahuauhauhauhauh
vou da uma estudada sobre isso.

Valeu pessoal....

Ate a proxima!

Criado 3 de outubro de 2009
Ultima resposta 5 de out. de 2009
Respostas 11
Participantes 5