Este exercício pede que seja desenvolvida uma hierarquia de classes que permita calcular o salário de três categorias diferentes de funcionários. Para cada uma das categorias o salário líquido é calculado de uma maneira diferente, entretanto, as três categorias também tem elementos em comum.

Nesta situação, a solução mais apropriada seria criar uma classe ancestral que contenha tudo que é comum para todas as categorias de funcionários e depois criar três classes descendentes que implementem o que é específico para cada categoria.

Projeto

Iniciamos criando um novo projeto que pode se chamar Tecelagem. Clique na opção de menú Arquivo/Novo projeto ou use as teclas de atalho Ctrl-Shift-N. Na primeira tela da criação do projeto selecione as opções Java e Aplicativo Java. Na segunda tela coloque o nome do projeto (Tecelagem) e verifique que a opção Criar classe principal esteja selecionada, depois clique em Finalizar.

O Netbeans irá criar o pacote principal e dentro dele a classe principal da aplicação. É essa classe que está declarada no arquivo que o netbeans apresenta na janela de código (figura 1).

tecelagem01.png
Figura 1 - Classe principal criada pelo Netbeans


A classe principal representa a aplicação como um todo. Pelas regras do java, dentro da classe principal de uma aplicação deve existir um método chamado main. O código neste método é o que será executado quando a aplicação iniciar. Veja que o Netbeans já cria também o método main dentro da classe principal, mas ele é deixado vazio, compete ao programador escrever o código que será executado.

Mas escrever o código do método main é a última parte do desenvolvimento, primeiro vamos criar as classes que irão representar os funcionários. Importante lembrar que devemos criar um arquivo para cada classe. Um erro um tanto comum é tentar criar várias classes no arquivo da classe principal. Esse arquivo já declara a classe principal, não use para nenhuma outra classe.

As novas classes devem ser criadas dentro do pacote principal. Para isso clique no pacote principal com o botão direito e escolha a opção Novo/Classe para cada nova classe que implementar.

Hierarquia de classes

Vamos começar criando a classe ancestral, a que vai conter o código comum para todas as classes. Vamos usar o nome Funcionario (sem acento) para esta classe. Importante lembrar que esta classe serve apenas para ser ancestral das outras, não deve ser instanciada, além disso pode ser necessário declarar métodos abstratos nela. Portanto vamos tornar a classe abstrata acrescentando a palavra abstract antes da palavra class na declaração. Neste ponto o arquivo deve parecer com o código abaixo.

Arquivo Funcionario.java:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tecelagem;
 
/**
 *
 * @author Usuario
 */
public abstract class Funcionario {
 
}
 
Podemos já criar as classes descendentes. Vamos chamá-las de FuncAdm, FuncProd e FuncVendas. Lembrando que como todas são classes descendentes da classe Funcionario, é preciso indicar a herança com a palavra reservada extends. Neste ponto os arquivos das classes descendentes devem parecer como abaixo.

Arquivo FuncAdm.java
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tecelagem;
 
/**
 *
 * @author Usuario
 */
public class FuncAdm extends Funcionario{
 
}
 

Arquivo FuncProd.java
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tecelagem;
 
/**
 *
 * @author Usuario
 */
public class FuncProd extends Funcionario{
 
}
 
Arquivo FuncVendas.java
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tecelagem;
 
/**
 *
 * @author Usuario
 */
public class FuncVendas extends Funcionario{
 
}
 

Atributos

Agora é o momento de cuidar dos atributos das classes. Os atributos comuns para todas as classes devem ficar na classe ancestral. Segundo o enunciado, esse é o caso do nome, RG e salário base. Para os funcionários administrativos é preciso fazer um controle de faltas, para os da produção um controle de horas trabalhadas e para os de vendas um controle de vendas realizadas. Abaixo os arquivos já com os atributos declarados. Os comentários originais foram removidos.

Arquivo Funcionario.java:
package tecelagem;
 
public abstract class Funcionario {
    protected String nome, RG;
    protected double salBase;
}
 
Arquivo FuncAdm.java
package tecelagem;
 
public class FuncAdm extends Funcionario{
    protected int faltas;
}
 
Arquivo FuncProd.java
package tecelagem;
 
public class FuncProd extends Funcionario{
    protected int horasDn, horasNt;
}
Arquivo FuncVendas.java
package tecelagem;
 
public class FuncVendas extends Funcionario{
    protected double totVendas;
}

Construtores

Os atributos de cada classe já estão definidos e distribuídos então agora podemos pensar na construção dos objetos e criar os construtores para todas as classes. Evidentemente vamos começar com a classe ancestral (Funcionário). O enunciado não cita os construtores que devem ser utilizados, mas analisando o tipo e propósito dos atributos da classe Funcionario podemos notar que todos eles precisam ser informados. Não há valor inicial ou valor default para atributos como nome e RG do funcionário. Nesse caso, o construtor mais indicado seria o parametrizado. Abaixo a classe Funcionario já com o construtor implementado.

Arquivo Funcionario.java:
package tecelagem;
 
public abstract class Funcionario {
    protected String nome, RG;
    protected double salBase;
 
    // Construtor
    public Funcionario(String nome, String RG, double salBase)
    {
        this.nome= nome;
        this.RG= RG;
        this.salBase= salBase;
    }
 
} // Classe Funcionario

Para as classes descendentes também é necessário criar construtores, mas estas classes definem um tipo diferente de atributos, são atributos que são parte de mecanismos implementados pela classe, cujo valor inicial é conhecido (zero). Estes atributos não precisam ser informados por parâmetros no construtor, basta que sejam inicializados.

Vale lembrar que ao implementar um construtor de uma classe descendente, o primeiro comando do construtor precisa ser uma chamada a algum construtor da classe ancestral. Nas classes abaixo isso é feito usando a referência super().

Arquivo FuncAdm.java
package tecelagem;
 
public class FuncAdm extends Funcionario{
    protected int faltas;
 
    // Construtor
    public FuncAdm(String nome, String RG, double salBase)
    {
        super(nome, RG, salBase);
        faltas= 0;
    }
 
} // FuncAdm
Arquivo FuncProd.java
package tecelagem;
 
public class FuncProd extends Funcionario{
    protected int horasDn, horasNt;
 
    // Construtor
    public FuncProd(String nome, String RG, double salBase)
    {
        super(nome, RG, salBase);
        horasDn= 0;
        horasNt= 0;
    }
} // FuncProd
Arquivo FuncVendas.java
package tecelagem;
 
public class FuncVendas extends Funcionario{
    protected double totVendas;
 
    // Construtor
    public FuncVendas(String nome, String RG, double salBase)
    {
        super(nome, RG, salBase);
        totVendas= 0;
    }
 
} // FuncVendas

Getters e setters

Agora podemos pensar nos getters e setters que serão implementados pelas classes. Assim como no caso dos construtores, é preciso avaliar quais atributos devem receber os métodos de acesso e quais não. No caso deste sistema, apenas os atributos da classe ancestral é que podem ser acessados externamente, os atributos das classes descendentes são parte de mecanismos internos e não podem ter seu valor modificado por entidades externas.

Arquivo Funcionario.java:
package tecelagem;
 
public abstract class Funcionario {
    protected String nome, RG;
    protected double salBase;
 
    // Construtor
    public Funcionario(String nome, String RG, double salBase)
    {
        this.nome= nome;
        this.RG= RG;
        this.salBase= salBase;
    }
 
    // Getters
    public String getNome() {
        return nome;
    }
 
    public String getRG() {
        return RG;
    }
 
    public double getSalBase() {
        return salBase;
    }
 
    // Setters
    public void setNome(String nome) {
        this.nome = nome;
    }
 
    public void setRG(String RG) {
        this.RG = RG;
    }
 
    public void setSalBase(double salBase) {
        this.salBase = salBase;
    }
 
} // Classe Funcionario

Métodos

Neste ponto só falta implementar os métodos solicitados para as classes. Como regra geral começamos sempre pela classe ancestral. Os métodos implementados na classe ancestral, ficam disponíveis para todas as classes descendentes. Por outro lado se um método precisa existir em todas as classes descendentes, então ele deve ser declarado na classe ancestral, mesmo que não possa ser implementado nela. É para isso que existem os métodos abstratos. Esse é o caso por exemplo dos métodos novoMes() e salarioLiquido(). Todas as classes descendentes devem ter estes métodos, então ele tem que ser declarado na classe Funcionario. Mas considerando que a classe não pode implementar os métodos, então eles devem ser abstratos na classe Funcionario e implementados pelas classes descendentes.

Já o método hollerith() é um outro caso, ele também precisa estar em todas as classes e portanto deve ser declarado na classe ancestral. Mas para este método é possivel fazer uma implementação parcial na classe Funcionario e completar a implementação nas classes descendentes. Então ele não será abstrato, mas será reescrito pelas classes descendentes aproveitando o código implementado pela classe ancestral.

Abaixo a classe Funcionario já na sua implementação final. Note que os dois métodos abstratos foram declarados no início da classe. Considerando que as classes descendentes são obrigadas a implementar estes métodos, é uma boa ideia deixa-los bem destacados.

Arquivo Funcionario.java:
package tecelagem;
 
public abstract class Funcionario {
    protected String nome, RG;
    protected double salBase;
 
    public abstract double salarioLiquido();
    public abstract void novoMes();
 
    // Construtor
    public Funcionario(String nome, String RG, double salBase)
    {
        this.nome= nome;
        this.RG= RG;
        this.salBase= salBase;
    }
 
    // Getters
    public String getNome() {
        return nome;
    }
 
    public String getRG() {
        return RG;
    }
 
    public double getSalBase() {
        return salBase;
    }
 
    // Setters
    public void setNome(String nome) {
        this.nome = nome;
    }
 
    public void setRG(String RG) {
        this.RG = RG;
    }
 
    public void setSalBase(double salBase) {
        this.salBase = salBase;
    }
 
    // método hollerit - implementação parcial
    public void hollerith()
    {
        System.out.println(
                "\n" +
                "Nome: " + nome + "\n" +
                "RG: " + RG + "\n" +
                "Salário base: " + salBase);
    }
} // Classe Funcionario
 

Agora a implementação dos métodos das classes descendentes. Vamos começar com a classe FuncAdm. Estes funcionários recebem um salário fixo, mas pode ocorrer de faltarem durante o mês então a quantidade de faltas precisa ser controlada. O enunciado pede que seja criado o método registrarFalta() que deve acrescentar uma falta para o funcionário, então podemos começar por aí.

Para estes funcionários o atributo do salário base é o valor bruto do salário. Para calcular o salário líquido basta fazer o desconto correspondente às faltas que tiver e é isso que o método salarioLiquido() deve fazer. Para poder separar o desconto do salario líquido no hollerit, foi implementado o método desconto() na classe.

O método hollerith() aproveita a implementação parcial da classe Funcionario e acrescenta as novas informações disponíveis da classe FuncAdm.

Abaixo a versão final das classes FuncAdm, FuncProd e FuncVendas.

Arquivo FuncAdm.java
package tecelagem;
 
public class FuncAdm extends Funcionario{
    protected int faltas;
 
    // Construtor
    public FuncAdm(String nome, String RG, double salBase)
    {
        super(nome, RG, salBase);
        faltas= 0;
    }
 
    public void registrarFalta() {
        faltas++;
    }
 
    public double desconto() {
        return (salBase/30) * faltas;
    }
 
    @Override
    public double salarioLiquido() {
        return salBase - desconto();
    }
 
    @Override
    public void novoMes() {
        faltas= 0;
    }
 
    @Override
    public void hollerith()
    {
        super.hollerith();
        System.out.println(
                "Faltas: " + faltas + "\n" +
                "Desconto: " + desconto() + "\n" +
                "Salário líquido: " + salarioLiquido() +
                "+-----------------------------------+\n");
     novoMes();
    }
} // FuncAdm

Arquivo FuncProd.java
package tecelagem;
 
public class FuncProd extends Funcionario{
    protected int horasDn, horasNt;
 
    // Construtor
    public FuncProd(String nome, String RG, double salBase)
    {
        super(nome, RG, salBase);
        horasDn= 0;
        horasNt= 0;
    }
 
   @Override
   public void novoMes(){
     horasDn = 0;
     horasNt = 0;
   }
 
   protected double valorHrDn(){
       return (horasDn * salBase);
   }
 
   protected double valorHrNt(){
       return (horasNt * salBase * 1.3);
   }
 
   @Override
   public double salarioLiquido(){
     return valorHrDn() + valorHrNt();
   }
 
   public void registrarHorasDiurnas(int horas){
     horasDn += horas;
   }
   public void registrarHorasNoturnas(int horas){
     horasNt += horas;
   }
 
   @Override
   public void hollerith(){
     super.hollerith();
     System.out.println(
              "Horas trabalhadas\n" +
              "      Diurnas : " + horasDn     + "\n" +
              "      Valor   : " + valorHrDn() + "\n" +
              "      Noturnas: " + horasNt     + "\n" +
              "      Valor   : " + valorHrNt() + "\n" +
              "Salário líquido: " + salarioLiquido() + "\n" +
              "+-----------------------------------+\n");
     novoMes();
   }
} // FuncProd

Arquivo FuncVendas.java
package tecelagem;
 
public class FuncVendas extends Funcionario{
    protected double totVendas;
 
    // Construtor
    public FuncVendas(String nome, String RG, double salBase)
    {
        super(nome, RG, salBase);
        totVendas= 0;
    }
 
    @Override
   public void novoMes(){
     totVendas = 0;
   }
 
   protected double valorComissao(){
       return totVendas * 0.03;
   }
 
   @Override
   public double salarioLiquido(){
     return salBase + valorComissao();
   }
 
   public void registrarVenda(double valor){
     totVendas += valor;
   }
 
   @Override
   public void hollerith(){
     super.hollerith();
     System.out.println(
        "Vendas no mês:" + totVendas + "\n" +
        "Comissão: " + valorComissao() + "\n" +
        "Salário líquido: " + salarioLiquido() + "\n" +
        "+-----------------------------------+\n");
     novoMes();
   }
} // FuncVendas

Método main

Abaixo o arquivo com a classe principal (Tecelagem.java) e o método main já com código para teste das classes.
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tecelagem;
 
/**
 *
 * @author Usuario
 */
public class Tecelagem {
 
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        // teste administrativos
        FuncAdm    adm = new FuncAdm("Huguinho", "1234-5", 3000.00);
        adm.registrarFalta();
        adm.registrarFalta();
        adm.hollerith();
 
        // teste produção
        FuncProd   prd = new FuncProd("Zezinho", "2345-1", 12.00);
        prd.registrarHorasDiurnas(150);
        prd.registrarHorasNoturnas(50);
        prd.hollerith();
 
        // teste vendas
        FuncVendas vnd = new FuncVendas("Luizinho", "3452-3", 700.00);
        vnd.registrarVenda(30000.00);
        vnd.registrarVenda(15000.00);
        vnd.hollerith();
    }
}