Bom, este post foi inspirado por um aluno do curso que eu ministrei fim de semana passado em Pato Branco/PR, mas este post não é culpa só dele 
O post é inspirado em coisas que eu já vi e já fiz muito por ai …
A idéia básica deste post é que:
Qualquer um pode escrever código que um computador entende, mas apenas um programador muito bom, escreve código que uma pessoa entende.
Como premissa básica, se você programa em uma linguagem, siga os padrões dela, por exemplo:
- Java
- Nomes de classes começam com letra maiúscula
- Nomes de variáveis com letras minúscula
- Classes nunca são criadas no package default, isto é desencorajado desde o java 1.3
- …
- Delphi
- Nomes de classes começam com T
- nomes de variáveis utilizam a notação ungara (se não estou enganado é este o nome)
- Se o campo é um text box, txtNome por exemplo
E assim por diante, ou seja, siga os padrões básicos da linguagem que estiver trabalhando.
Alem dos padrões das linguagens, cada empresa normalmente possui um padrão de formatação, por tanto, siga o padrão da empresa, e de preferência, configure a sua IDE para te ajudar neste trabalho.
- Nomes de classes obrigatoriamente precisam informar a quem lê, do que se trata esta classe.
- Classes preferencialmente possuem comportamento.
- “Coisas” no código fonte, devem ser agrupados por domínio de comportamento.
- Aprenda com os padrões de outras linguagens, por exemplo, o principio DRY (Don`t Repeat Yourself) do Ruby On Rails, deve ser utilizado sempre em qualquer situação, independente da linguagem que esta sendo utilizada.
- Métodos grandes são difíceis de ler, se um método estiver muito grande, com certeza absoluta você pode dividi-lo em métodos menores.
- Nomes de métodos precisam, obrigatoriamente, dizer exatamente o que este método faz.
- Classes não são tabelas, por tanto, se estiver criando uma classe persistente, ela pode ter um “id”, por exemplo a classe Cliente, pode ter um campo “id”, mas pelo amor de Jahl, ela não pode, em ipótese alguma, ter um campo “clienteid”.
- Classes, por serem a forma de se definir um objeto, que por definição, possui estado e comportamento, 99% das vezes, possuem mais metodos do que “gets” e “sets”, se você olhar para uma classe que não os possui, provavelmente você os colocou no lugar errado.
- Não crie “gets” e “sets” indiscriminadamente, crieos apenas quando forem necessários, muitos dos atributos de uma classe não devem ser alterados externamente.
Refactoring é seu amigo!
Refatore sempre que precisar para melhorar a legibilidade do código, ou quando perceber que alguma coisa esta implementada no lugar errado.
A boa noticia é que a maioria das IDEs, para diversas linguagens, tem suporte para diversos tipos refactoring.
A má noticia é que se a sua IDE ou linguagem, não tem suporte a refatoring, ou não tem suporte ao refactoring que você precisa, você vai ter que faze-lo manualmente.
DRY (Don’t Repeat Yourself)
Não se repita, se você escreveu alguma coisa em algum lugar, reuse, não escreva novamente!
Mas preste atenção, reuso não é igual a copiar e colar, se você precisar copiar e colar, é por que provavelmente fez errado da primeira vez.
Considere este exemplo (Retirado do livro encontrado aqui)
public class BookRental {
String id;
String customerName;
...
}
public class BookRentals {
private Vector rentals;
public String getCustomerName(String rentalId) {
for (int i = 0; i < rentals.size(); i++) {
BookRental rental = (BookRental) rentals.elementAt(i);
if (rental.getId().equals(rentalId)) {
return rental.getCustomerName();
}
}
throw new RentalNotFoundException();
}
}
public class RentalNotFoundException extends Exception {
...
}
Agora imagine que você precisa adicionar um método para remover um aluguel pelo ID:
public class BookRentals {
private Vector rentals;
public String getCustomerName(String rentalId) {
for (int i = 0; i < rentals.size(); i++) {
BookRental rental = (BookRental) rentals.elementAt(i);
if (rental.getId().equals(rentalId)) {
return rental.getCustomerName();
}
}
throw new RentalNotFoundException();
}
public void deleteRental(String rentalId) {
for (int i = 0; i < rentals.size(); i++) {
BookRental rental = (BookRental) rentals.elementAt(i);
if (rental.getId().equals(rentalId)) {
rentals.remove(i);
return;
}
} throw new RentalNotFoundException();
}
}
Você acha que este código esta OK? lembra-se do DRY? neste exemplo simples ja temos muito código duplicado, o que com certeza não é bom, então, podemos fazer o seguinte refactoring:
public class BookRentals {
private Vector rentals;
public String getCustomerName(String rentalId) {
int rentalIdx = getRentalIdxById(rentalId);
return ((BookRental) rentals.elementAt(rentalIdx)).getCustomerName();
}
public void deleteRental(String rentalId) {
rentals.remove(getRentalIdxById(rentalId));
} private int getRentalIdxById(String rentalId) {
for (int i = 0; i < rentals.size(); i++) {
BookRental rental = (BookRental) rentals.elementAt(i);
if (rental.getId().equals(rentalId)) {
return i;
}
}
throw new RentalNotFoundException();
}
}
Por que remover código duplicado?
Em geral, se um código esta duplicado em 10 lugares, se você precisar alterar este código precisara lembrar de todos os 10 lugares, o qe provavelmente não vai acontecer, por tanto vai criar bugs no sistema.
O seu código não precisa de comentários! des de que ele seja legivel!
Considere este outro exemplo do mesmo livro, em que é desenvolvida uma aplicação para gerenciamento de conferencias, e que existe uma barra lateral onde devem ser exibidas informações sobre o participante:
//It stores the information of a participant to be printed on his badge.
public class Badge {
String pid; //participant ID
String engName; //participant's full name in English
String chiName; //participant's full name in Chinese
String engOrgName; //name of the participant's organization in English
String chiOrgName; //name of the participant's organization in Chinese
String engCountry; //the organization's country in English
String chiCountry; //the organization's country in Chinese
//***********************
//constructor.
//The participant ID is provided. It then loads all the info from the DB.
//***********************
Badge(String pid) {
this.pid = pid;
//***********************
//get the participant's full names.
//***********************
ParticipantsInDB partsInDB = ParticipantsInDB.getInstance();
Participant part = partsInDB.locateParticipant(pid);
if (part != null) {
//get the participant's full name in English.
engName = part.getELastName() + ", " + part.getEFirstName();
//get the participant's full name in Chinese.
chiName = part.getCLastName()+part.getCFirstName();
//***********************
//get the organization's name and country.
//***********************
OrganizationsInDB orgsInDB = OrganizationsInDB.getInstance();
//find the ID of the organization employing this participant.
String oid = orgsInDB.getOrganization(pid);
if (oid != null) {
Organization org = orgsInDB.locateOrganization(oid);
engOrgName = org.getEName();
chiOrgName = org.getCName();
engCountry = org.getEAddress().getCountry();
chiCountry = org.getCAddress().getCountry();
}
}
}
...
}
Agora transforme os comentários em código, fazendo-o tão simples quanto os comentários.
//It stores the information of a participant to be printed on his badge.
public class Badge {
...
}
Para que precisamos deste comentário se podemos escrever assim:
public class ParticipantInfoOnBadge {
...
}
E nesta parte:
public class ParticipantInfoOnBadge {
String pid; //participant ID
String engName; //participant's full name in English
String chiName; //participant's full name in Chinese
String engOrgName; //name of the participant's organization in English
String chiOrgName; //name of the participant's organization in Chinese
String engCountry; //the organization's country in English
String chiCountry; //the organization's country in Chinese
...
}
Por que não transformas os comentários em variáveis assim:
public class ParticipantInfoOnBadge {
String participantId;
String participantEngFullName;
String participantChiFullName;
String engOrgName;
String chiOrgName;
String engOrgCountry;
String chiOrgCountry;
...
}
Ou destes comentários?
public class ParticipantInfoOnBadge {
...
//***********************
//constructor.
//The participant ID is provided. It then loads all the info from the DB.
//***********************
ParticipantInfoOnBadge(String pid) {
this.pid = pid;
...
}
}
Se podemos transforma-los em nomes de parametros:
public class ParticipantInfoOnBadge {
...
//***********************
//constructor.
//It loads all the info from the DB.
//***********************
ParticipantInfoOnBadge(String participantId) {
this.participantId = participantId;
...
}
}
Podemos tambem transformar comentários em partes do código do método:
public class ParticipantInfoOnBadge {
...
//***********************
//constructor.
//***********************
ParticipantInfoOnBadge(String participantId) {
loadInfoFromDB(participantId);
} void loadInfoFromDB(String participantId) {
this.participantId = participantId;
...
}
}
Agora pegue este outro fragmento de código:
void loadInfoFromDB(String participantId) {
this.participantId = participantId;
//***********************
//get the participant's full names.
//***********************
ParticipantsInDB partsInDB = ParticipantsInDB.getInstance();
Extract some code to form a method and use the comment to name the method 41
Licensed for viewing only. Printing is prohibited. For hard copies, please purchase from www.agileskills.org
Participant part = partsInDB.locateParticipant(participantId);
if (part != null) {
//get the participant's full name in English.
engFullName = part.getELastName() + ", " + part.getEFirstName();
//get the participant's full name in Chinese.
chiFullName = part.getCLastName()+part.getCFirstName();
//***********************
//get the organization's name and country.
//***********************
OrganizationsInDB orgsInDB = OrganizationsInDB.getInstance();
//find the ID of the organization employing this participant.
String oid = orgsInDB.getOrganization(participantId);
if (oid != null) {
Organization org = orgsInDB.locateOrganization(oid);
engOrgName = org.getEName();
chiOrgName = org.getCName();
engOrgCountry = org.getEAddress().getCountry();
chiOrgCountry = org.getCAddress().getCountry();
}
}
}
E me diga por que não transformas os comentários em nomes de métodos assim:
void loadInfoFromDB(String participantId) {
this.participantId = participantId;
getParticipantFullNames();
//***********************
//get the organization's name and country.
//***********************
//find the ID of the organization employing this participant.
OrganizationsInDB orgsInDB = OrganizationsInDB.getInstance();
String oid = orgsInDB.getOrganization(participantId);
if (oid != null) {
Organization org = orgsInDB.locateOrganization(oid);
engOrgName = org.getEName();
chiOrgName = org.getCName();
engOrgCountry = org.getEAddress().getCountry();
chiOrgCountry = org.getCAddress().getCountry();
}
} void getParticipantFullNames() {
ParticipantsInDB partsInDB = ParticipantsInDB.getInstance();
Participant part = partsInDB.locateParticipant(participantId);
if (part != null) {
//get the participant's full name in English.
engFullName = part.getELastName() + ", " + part.getEFirstName();
//get the participant's full name in Chinese.
chiFullName = part.getCLastName()+part.getCFirstName();
}
}
E assim por diante, seguindo algumas regras básicas, o código vai ser bastante legível, dispensando boa parte dos comentários, o que vai fazer com que o seu desenvolvimento seja mais rápido, e vai também fazer com que outras pessoas consigam ler o seu código sem problemas.
Mas eu não estou dizendo que não é para comentar nada, mas com certeza, boa parte os comentários é dispensavel, se o seu código for legível!
Bom, acho que é isto por enquanto …
Por favor, contribua sua ideia para melhorar este manifesto, e se gostou dele,divulge-o, isto ainda vai lhe poupar muito tempo!
<MODO SARCASMO ON>
Apenas para deixar claro, a maior parte das regras definidas neste post, esta acima de qualquer contestação, e é por definição, verdade absoluta. (Ou seja, até que se prove ao contrário, estas regras estão acima de contestação).
</MODO SARCASMO ON>
Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!