Skip to content

Instantly share code, notes, and snippets.

@gustavomeloGH
Last active November 26, 2019 13:16
Show Gist options
  • Save gustavomeloGH/427a01e4475fc4c4357c4e849b277392 to your computer and use it in GitHub Desktop.
Save gustavomeloGH/427a01e4475fc4c4357c4e849b277392 to your computer and use it in GitHub Desktop.
Guia de Boas Práticas de Programação

Guia de Boas Práticas de Programação

Índice:

Obs.: Todos os exemplos está na linguagem Java

1. Nomenclatura

Use nomes relevantes, significativos e de fáceis entendimento para atribuir à classes, funções, variáveis, etc.
Obs.: Se um nome exigir um comentário, não há clareza de seu propósito. Refatore para um nome mais significativo.

👎 Bad Code 👍 Code Clean Rules
int dsc int daysSinceCreation Não use nomes inteiros com abreviações
String string2 String legalDocument Não use nomes genéricos demais
User[] userList User[] users Seja coeso (Array != List)
List theUserListWithAllUsersIncluded List allUsers Seja objetivo (evite nomes enormes)
boolean valid boolean isValid Use éAlgo (isSomething)
boolean value() boolean hasValue() Use hasAlgo (hasSomething)


1.1. Use nomes significativos em seus contextos próprios:

Não recomendado:

String addressNumber
String addressStreet
String addressCity

Recomendado:

class Address { 
  ... 
  String number;
  String street;
  String city;
}


1.2. Padronize os nomes, não misture conceitos:

Não recomendado:

void loadData()
void fetchDataFiltered()
void getAllData()

Recomendado:

void getData()
void getFilteredData()
void getAllData()
  • O mesmo conceito de "get" foi utilizado para "load” e “fetch”.


1.3. Prefira usar notações para explicar seu código:

Não recomendado:

for (int i=0; i <= 7; i++) {
  if ( i == 6 ) {
    System.out.println(w[i]);
  }
}

Recomendado:

final int NUMBER_DAYS_WEEK = 7;
final int SUNDAY = 6;

... {
  for (int i=0; i <= NUMBER_DAYS_WEEK; i++) {
    if ( i == SUNDAY ) { 
       System.out.println(daysOfTheWeek[i]);
    }
  }
 
  • É adicionado mais linhas, entretanto, perceba que o código fica mais legível.


1.4. Use verbos para nomes de funções e substantivos para nome de classes e variáveis

Não recomendado:

public class Calculate {
  public void addition() {
  ...
  }
}

Recomendado:

public class Calculator {
  public void sum() {
  ...
  }
}


1.5. Priorize nomes que expressem sentido positivo

Não recomendado:

void convert() throws Exception {
  if (shoudNotConvert()) {
    throw new CantConvertException();
  }
  ...
}

Recomendado:

void convert() throws Exception {
  if (canConvert()) {
      ...
  } else {
    throw new CantConvertException();
  }
}
  • Afirmações negativas são um pouco mais difíceis para entender do que a afirmativas.

2. Funções/Métodos

É recomendável que uma função/método seja curto. Qualquer método com mais de dez linhas, é interessante questionar se precisa refatorar para melhor compreensão deste.


2.1. Extração: Quebre uma método grande em métodos menores

  • Desta maneira, aumenta a compreensão e leitura.

Não recomendado:

    
public class Product {
  // ...

  public double calculateTotal() {
    double total = product.quantity * product.price;
    if (total > 200) {
    	total *=  0.85;
    }
    return total;
  }               

Recomendado:

public class Product {
  // ...

  public double calculateTotal() {
    double total = calculatePriceByQuantity();
    total = applyDiscounts(total);
    return total;
  }
  
  private double calculatePriceByQuantity() {
     return product.quantity * product.price;
  }

  private double applyDiscounts(double total) {
    return (total > 200) ? (total * 0.85) : total;
  }


2.1.2 Extração: Decomponha as condições

Não recomendado:

boolean hasValidLegalDocument(String doc) {
  if (11 === doc.length || 14 == doc.length) {
  ...
}

Recomendado:

final int CPF_SIZE = 11;
final int CNPJ_SIZE = 14;

boolean hasValidLegalDocument(String doc) {
  if (hasLegalDocProperSize(doc.length)) {
  ...
}

boolean hasLegalDocProperSize(final int length) {
   return (CPF_SIZE == length || CNPJ_SIZE == length);
}
  


2.2 Evite muitos parâmetros, preserve os objetos

Não recomendado:

PagedList<User> list() {
  ...
  return page(form.start, form.pageSize, form.search, form.field);
}

PagedList<User> page(int start, int pageSize, String search, String field) { ... }

Recomendado:

PagedList<User> list() {
  ...
  return page(form);
}

PagedList<User> page(SearchForm form) { ... }


2.3 Evite - quando possível - retornar nulo

Não recomendado:

public List<User> getActiveUsers() {
  return repository.getActiveUsers();
}

Recomendado:

public List<User> getActiveUsers() {
  List<User> users = repository.getActiveUsers();
  return null == users ? Collections.<User>emptyList() : users;
}
  • Uma excelente alternativa, seria utilizando o tipo Nullable, dependendo da linguagem.


2.4 Evite duplicação de códigos

Não recomendado:

public void save() throws Exception {
   if (null == this.name) {
       throw new Exception("Faltando preencher o campo: Nome");
   }
   if (null == this.description) {
       throw new Exception("Faltando preencher o campo: Descrição");
   }
   repository.save(this);
}

public void update() throws Exception {
   if (null == this.name) {
       throw new Exception("Faltando preencher o campo: Nome");
   }
   if (null == this.description) {
       throw new Exception("Faltando preencher o campo: Descrição");
   }
   repository.update(this);
}

Recomendado:

public void save() throws Exception {
   if (hasPropertiesChecked())
      repository.save(this);
}

public void update() throws Exception {
   if (hasPropertiesChecked()) 
      repository.update(this);
   
}

private boolean hasPropertiesChecked() throws Exception {
     String final defaultMessage = "Faltando preencher o campo: %s";
     if (null == this.name) {
         throw new Exception(String.formar(defaultMessage, "Nome");
     }
     if (null == this.description) {
         throw new Exception(String.formar(defaultMessage, "Descrição");
     }
    return true;
 }


2.5. Parametrize funções/métodos similares e evite repetições

Não recomendado:

double tenPercentGain() { ... } 
double ninePercentGain() { ... } 
double twoPercentGain() { ... } 

Recomendado:

double calculePercentGain(double percent) { ... } 

3. Classes e Entidades: Programação Orientada a Objetos - SOLID

Tem-se os 5 princípios do SOLID, como boas práticas para Programação Orientada a Objetos (P.O.O.).


3.1. 'S' (SRP) - Princípio da Responsabilidade Única

Uma classe deve ter somente uma razão para mudar.

Não recomendado:

public class Establishment {
    public double calculateDiscount(Sale sale) { ... }
    ...
}

public class Sale {
  ...
}

Recomendado:

public class Establishment {
    public double getSaleDiscount() {
      return this.getSale().calculateDiscount();
    }
}

public class Sale {
  public double calculateDiscount() { ... }
   ...
}

Não recomendado:

class Person {
    @Override
    toString() { ... }
    Person save(Person person) { ... } 
}

Recomendado:

class Person {
    @Override
    String toString() { ... }
    Person save(Person person) { ... } 
}

class PersonDB {
    Person save(Person person) { ... } 
}

Evite:

public void sendEmail(to, from, text) {
   ...
}

Use:

public class Email {
   String to;
   String from;
   String text;
   
   public void send() {
        ...
   }
}


3.2. 'O' (OCP) - Princípio Aberto-Fechado

  • Entidades de software (classes, módulos, funções, etc.) devem ser abertas para extensão, mas fechadas para modificação.
  • Use Classes Abstratas e polimofismo para resolver estes problemas!

Não recomendado:

class File { ... }

class WordFile {
  public void generateWord() { ... }
}

class PdfFile {
  public void generatePdf() { ... }
}

class FileController {
   public void generateFiles(IList<File> fileList) {
      fileList.forEach(file -> {
        if (file instanceof WordFile) {
           ((WordFile) file).generateWord();
        } else if (file instanceof PdfFile) {
           ((PdfFile) file).generatePdf();
        }
        // Cada novo arquivo, será adicionada mais uma condição
      });
   }
}

Recomendado:

abstract class File { 
  public void generate();
}

class WordFile extends File {
  @Override
  public void generate() { ... }
}

class PdfFile extends File {
  @Override
  public void generate() { ... }
}

class TextFile extends File {
  @Override
  public void generate() { ... }
}


class FileController {
   public void generateFiles(IList<File> fileList) {
      fileList.forEach(file -> file.generate());
   }
}


3.3. 'L' (LSP) - Princípio de Substituição de Liskov

Tipos derivados devem ser completamente substituíveis por seus tipos de base.

Não recomendado:

class Rectangle {
  private int width;
  private int height;
  // getters e setters mothods...
}

class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        super.width(width);
        super.height(width);
    }
    @Override
    public void setWeight(int length) {
        super.width(length);
        super.height(length);
    }
}

Recomendado:

class Rectangle {
  private int width;
  private int height;
  // getters, setters mothods...
}

class Square {
  private int side;
  // get, set mothods...
}
    1. Uma classe não pode utilizar herança pelo simples fato de ter algo em comum.
    1. Um retângulo é um quadrado, porém não o inverso. Em caso de polimorfismo, o exemplo acima - não recomendado - surtirá efeitos colaterais.


3.4. 'I' (ISP) - Princípio da segregação de interfaces

  • Uma interface deve ser quebrada em interfaces menores, evitando implementação forçada de todos os seus métodos por causa do contrato.

Não recomendado:

interface ITelephone {
  void call();
  void turnOnOff();
  void takePicture() throws NotImplementedException;
}

class Phone implements ITelephone {
  void call() { ... }
  void turnOnOff() { ... }
  void takePicture() throws NotImplementedException 
    { throw new NotImplementedException(); }
}

class CellPhone implements ITelephone {
  void call() { ... }
  void turnOnOff() { ... }
  void takePicture() { ... }
}

Recomendado:

interface ITelephone {
  void call();
  void turnOnOff();
}

interface ICellPhone extends ITelephone {
  void takePicture();
}

class Telephone implements ITelephone {
  void call() { ... }
  void turnOnOff() { ... }
}

class CellPhone implements ICellPhone {
  void call() { ... }
  void turnOnOff() { ... }
  void takePicture() { ... }
}


3.5. 'D' (DIP) - Princípio da Inversão de Dependência

Não recomendado:

class Service {
   FirstSender firstSender;
   SecondSender secondSener;
   
   public void send (Message message) {
      if (message.type == FIRST) {
        new FirstSender().send(message);
      } 
      else (message.type == SECOND) {
        new SecondSender().send(message);
      }
      ...
   }
}

Recomendado:

interface ISender {
  send(Message message);
}

class SenderFactory {
    ISender sender = null;
    
    public static ISender create (Type type) {
      if (type == FIRST) {
        sender = new FirstSender();
      } 
      else (type == SECOND) {
        sender = new SecondSender();
      }
      ...
      return sender;
   }

}

class Service {
   void send (Message message) {
      SenderFactory.create(message.type).send(message);
   }
}
  • Utilizar Injeção de Dependência (se possível), também ajuda no desacoplamento dessas classes.


3.6. Padrão BUILD para construção de uma classe

Não recomendado:

public class Person {
    private String firstName;
    private String middleName;
    private String lastName;
    private int age;

    public Person(String firstName, String middleName, String lastName, int age) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
        this.age = age;
    }
    public Person(String firstName, String lastName, int age) {
        this(firstName, null, lastName, age);
    }
    public Person(String firstName, int age) {
        this(firstName, null, age);
    }
    
    // getters e setters...
}

Recomendado:

public class Person {
    private String firstName;
    private String middleName;
    private String lastName;
    private int age;

    public static class Builder {
        // Obrigatórios
        private String firstName;
        private int age;
        
        // Opcionais
        private String middleName;
        private String lastName;
      
        // Obrigatórios
        public Builder(String firstName, int age){
           this.firstName = firstName;
           this.age = age;
        }
        
        // Opcionais
        public Builder middleName(String middleName) {
          this.middleName = middleName;
          return this;
        }
        
        public Builder lastName(String lastName) {
          this.lastName = lastName;
          return this;
        }
        
        public Person build() {
          return new Person(this);
        }
    }
    
    private Person(Builder builder) {
        firstName =  builder.firstName;
        age =        builder.age;
        middleName = builder.middleName;
        lastName =   builder.lastName;
    }    
}

...
Person person = new Person.Builder("Gustavo", 23)
                          .middleName("Melo")
                          .lastName("Silva")
                          .build();
  • Padrão válido apenas quando existem atributos opcionais.


4. Bibliografia e recomendações de leitura

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment