AB2 - Projeto de Software


EM DESENVOLVIMENTO 🧑‍🔧


👩‍💻 Objetivo:

O objetivo do projeto é implementar melhorias em um sistema de folha de pagamento desenvolvido da AB1.

😖 Code Smells:

Primeiramente, vamos navegar pelo sistema anterior, classe por classe, a fim de identificar os code smells presentes no código.


Code smells procurados
Duplicated Code
Long Parameter List
Long Method
Large Class
Divergent Change
Shotgun Surger
Feature Envy
Primitive Obsession
Lazy Class
Speculative Generality
Message Chains
Middle Man
Indecent Exposure
Data Class
Refused Bequest
Comments

Os code smells identificados podem ser vistos aqui

🔧 Refatoramento:

  • Strategy

    • Quando um novo empregado é adicionado, ele pode ser de 3 tipos, horista, assalariado ou comissionado. Para isso foi criado um método add()na classe principal Employee e um relacionamento hierárquico entre as subclasses Hourly, Comissionede Assalaried.
    • Pôde ser observado 3 tipos de code smells ao fazer o encaminhamento adequado para a criação dos objetos. É interessante observar que a implementação desse padrão de projeto lidou com esses 3 problemas do sistema anterior.
    • ANTES:
    if(type.equals("assalariado")) {
        	System.out.println("Informe o salário:");
        	salary = in.nextDouble();
        	Salaried salaried =  new Salaried(name, adress, id, paymentMethod, salary, sindicate);
        	listEmployees.add(salaried);
        } else if (type.equals("horista")) {
        	System.out.println("Informe o salário horário:");
        	salary = in.nextDouble();
        	Hourly hourly = new Hourly(name, adress, id, paymentMethod, salary, sindicate);
        	listEmployees.add(hourly);
        } else {
        	System.out.println("Informe a porcentagem de comissão:");
        	double percent = in.nextDouble();
        	System.out.println("Informe o salário:");
        	salary = in.nextDouble();
        	Comissioned comissioned = new Comissioned(name, adress, id, paymentMethod, salary, percent, sindicate);
        	listEmployees.add(comissioned);
        } 
    
    //log method: uso complexo de condicionais
    //exposição indevida: declaração das subclasses ao invés da principal (ex: Hourly hourly = new Hourly(name, adress, id, paymentMethod, salary, sindicate);)
    //long parameter list: as variáveis salaried, hourly e comissioned  não precisavam ser criadas, visto que o construtor poderia ser passado diretamente em listEmployees.add();
    • A fim de resolver os problemas mencionados, foi aplicada uma estratégia primeiramente criando um map: employeeMap.put("tipo de empregado", posição);. Depois, foi criada uma enumchamada StrategyEmployeecom os métodos referentes a criação de cada tipo de employee e acessada de acordo com a posição no map. Para dar suporte a essa estratégia, também foi criada uma classe abstrata ChoiceEmployee e os métodos de StrategyEmployees eram desse tipo.
    • StrategyEmployee
    • ChoiceEmployee
    • DEPOIS:
    HashMap<String, Integer> employeeMap = new HashMap();
        employeeMap.put("horista", 0);
        employeeMap.put("assalariado", 1);
        employeeMap.put("comissionado", 2);
    
    listEmployees.add(StrategyEmployees.values()[employeeMap.get(type)].getChoiceEmployee(name,adress,id,sindicate,paymentMethod).choiceEmployee());
    
    //As condicionais anteriores foram substituídas por essas linhas de código na função add
  • Chain Constructors

    • Em Employee, no sistema anterior, era possível encontar duplicated code em funções construtoras semelhantes e para solucionar isso foi implementado o padrão de projeto: Chain Constructors. Isso, selecionando o construtor com maior número de parâmetros na hora de invocá-los.
    • ANTES:
     public Employee(){
    	
    }
    
    public Employee(String name, String adress, int id, String paymentMethod, int sindicate) {
        this.name = name;
        this.adress = adress;
        this.id = id;
        this.paymentMethod = paymentMethod;
        this.sindicate = sindicate;
    }
    
    public Employee(String name, String adress, int id) {
        this.name = name;
        this.adress = adress;
        this.id = id;
    }
    public Employee(){}
    
    public Employee(String name, String adress, int id, String paymentMethod, int sindicate) {
       this.name = name;
       this.adress = adress;
       this.id = id;
       this.paymentMethod = paymentMethod;
       this.sindicate = sindicate;
    }
    
    public Employee(String name, String adress, int id) {
       this(name, adress, id, null, -1);
    }
    //como *public Employee(){}* já retorna null por padrão, achei válido não aplicar o padrão
  • Long Parameter List (code smell)

    • Algumas variáveis que não precisavam ser criadas foram excluídas.
    • Em Employeeisso aconteceu na função idGenerate()
    • Antes X Depois
    //antes
        public int idGenerate() {
    	Random random = new Random();
    	int id = random.nextInt(1000) + random.nextInt(35) + random.nextInt(849);
    	int size = listEmployees.size();
    	for(int i = 0; i < size; i++) {...}
    	return id;
    
    //depois
    
     public int idGenerate() {
    	Random random = new Random();
    	int id = random.nextInt(1000) + random.nextInt(35) + random.nextInt(849);
    	for(int i = 0; i < listEmployees.size(); i++) {}
    	return id;
    }
    }
    • Em Sindicateisso também aconteceu e foi modificado pelo mesmo motivo de criar uma variável size ao invés de utilizar o retorno da função .size().
    • Continuando em Sindicate, outras duas variáveis foram excluídas.
    • Antes X Depois
    //antes
    public void addSindicateMember(String name, int employeeId) { 
    	int id = idGenerate();
    	Sindicate member = new Sindicate(name, employeeId, id, 0, 0);
    	listSindicate.add(member);
    	System.out.println(listSindicate.get(listSindicate.size() - 1).showSindicateMember(member));
    	System.out.println("Adição no sindicato realizada com sucesso!");
    }
    
    //depois, com a remoção da variável *id* e *member*
    //duas linhas a menos
    public void addSindicateMember(String name, int employeeId) { 
    	listSindicate.add(new Sindicate(name, employeeId, idGenerate(), 0, 0));
    	System.out.println(listSindicate.get(listSindicate.size() - 1).showSindicateMember(listSindicate.get(listSindicate.size() - 1)));
    	System.out.println("Adição no sindicato realizada com sucesso!");
    }
  • Outros code smells resolvidos

    • Para outros code smells presentes no relatório não consegui achar um padrão de projeto em que eles se encaixassem, mas foram mudanças bem simples.
    • Algumas funções inutilizadas de get() e set()foram deletadas.
    • Além disso, a classe Textse encaixava em Speculative Generality e foi melhorada para cumprir seu papel de maneira eficiente e não apenas especular sobre ele. Por isso, foram adicionados novos métodos como: changeEmployee(), undoRedo() e newCicleOrExit().
    • Os packages também estavam mal organizados e alguns só possuiam apenas uma classe. Para resolver isso, as classes "filhas únicas" foram agrupadas em um package chamado utils.