Refactoring e Code Smells: Seu código está apodrecendo!
-
Upload
emanuel-canuto -
Category
Software
-
view
222 -
download
3
description
Transcript of Refactoring e Code Smells: Seu código está apodrecendo!
Tava precisando!
https://www.facebook.com/photo.php?fbid=10203189806695222&set=p.10203189806695222&type=1&permPage=1
@handersonbf
RigidezParece simples, mas difícil de mudar
Mudanças em cascata
Dependências entre módulos
Medo de correção de bugs
FragilidadeCode and Fix
Manutenção Adventure
Desenvolvimento fora de controle
Credibilidade começa a cair
Ei mah, tua feature quebrou!
Terminei a feature!!!
ImobilidadeNão é possível reutilizar software
Muitas Dependências
Responsabilidades de mais
Risco alto para reutilizar
“Refactoring is a disciplined technique for restructuring an
existing body of code, altering its internal structure without
changing its external behavior.”
Martin Fowler
By continuously improving the design of code, we make it easier and easier to work with. This is in
sharp contrast to what typically happens: little refactoring and a great deal of attention paid to
expediently adding new features. If you get into the hygienic habit of refactoring continuously, you'll find that it is easier to extend and maintain code.
Joshua Kerievsky
Long MethodThe object programs that live best and longest are those with short
methods.
[…]
The key here is not method length but the semantic distance between what the method does and how it does it.
[…]
Long Method[…]
Development environments that allow you to see two methods at once help to eliminate this step, but the real key to making it easy to
understand small methods is good naming.
[…]
public int fechaFolha(String folha) {// imprime a folha adicionando bla bla blatry {
FileReader arq = new FileReader(folha);BufferedReader lerArq = new BufferedReader(arq);String linha = lerArq.readLine();while (linha != null) {
System.out.printf("%s\n", linha);this.linha += linha;linha = lerArq.readLine();
}arq.close();
} catch (IOException e) {System.err.printf("Erro na abertura do arquivo: %s.\n",e.getMessage());
}// verifica se a folha é especialif (linha.contains("ARS0010304")) {
this.folhaEspecial = true;}
...
public int fechaFolha(String folha) { imprimeFolha(folha);ehFolhaEspecial();atualizaDadosDePagamento();
}
public int fechaFolha(String folha) { imprimeFolha(folha);eFolhaEspecial();atualizaDadosDePagamento();
}private void ehFolhaEspecial() {
if (linha.contains("ARS0010304")) {this.folhaEspecial = true;
}}private void imprimeFolha(String folha) {
try {FileReader arq = new FileReader(folha);BufferedReader lerArq = new BufferedReader(arq);String linha = lerArq.readLine();while (linha != null) {
System.out.printf("%s\n", linha);this.linha += linha;linha = lerArq.readLine();
}...
I throw away commented code
Few Short Methods Per Class★ Fácil de testar.★ Fácil para reusar.★ Fácil para modificar.★ Menos bugs são descoberto, estatisticamente em métodos
curtos e classes curtas.★ Equipe de desenvolvimento coda mais rápido, porque há menos
necessidade de refactoring.
Shotgun Surgery
You whiff this when every time you make a kind of change, you have to make a lot of little changes to a lot of different classes. When the changes are all over the place, they are hard to find, and it's easy to miss an important change.
Feature Envy
A classic smell is a method that seems more interested in a class other than the one it actually is in.
[ . . . ]
The most common focus of the envy is the data.
Feature Envy
Mova o método quando todo o método quer estar claramente em outro lugar, ou…
Extraia o método quando apenas uma parte do método é invejoso, ou…
Extraia a classe se você tem vários métodos invejosos e a funcionalidade não chega a pertencer a outro objeto.
Long Parameter List
public Object method(int var,int var1,int var2,int var3,int var4,int var5,int var6,int var7) {
...}
Long Parameter List[…]
long parameter lists are hard to understand, because they become inconsistent and difficult to use, and because you are forever
changing them as you need more data.
[…]
Duplicated Code
Long MethodLarge Class
Shotgun SurgeryLong Parameter List
Divergent Change
Parallel Inheritance Hierarchies
Switch Statements
Primitive Obsession
Data Clumps
Feature Envy
Incomplete Library ClassAlternative Classes with Different Interfaces
Inappropriate Intimacy
Middle Man
Message Chains
Temporary Field
Speculative Generality
Lazy ClassComments
Refused Bequest
Data ClassBAD SMELLSBAD SMELLS
Extract MethodMotivation
Extract Method is one of the most common refactorings I do. I look at a method that is too long or look at code that needs a comment to understand its purpose. I then turn that fragment of code into its own method.
I prefer short, well-named methods for several reasons. First, it increases the chances that other methods can use a method when the method is finely grained. Second, it allows the higher-level methods to read more like a series of comments. Overriding also is easier when the methods are finely grained.
It does take a little getting used to if you are used to seeing larger methods. And small methods really work only when you have good names, so you need to pay attention to naming. People sometimes ask me what length I look for in a method. To me length is not the issue. The key is the semantic distance between the method name and the method body. If extracting improves clarity, do it, even if the name is longer than the code you have extracted.
void printOwing() {
Enumeration e = _orders.elements();double outstanding = 0.0;
System.out.println("**************************");System.out.println("***** Customer Owes ******");System.out.println("**************************");
while (e.hasMoreElements()) {Order each = (Order) e.nextElement();outstanding += each.getAmount();
}
System.out.println("name:" + _name);System.out.println("amount" + outstanding);
}
void printOwing() {
Enumeration e = _orders.elements();double outstanding = 0.0;
System.out.println("**************************");System.out.println("***** Customer Owes ******");System.out.println("**************************");
while (e.hasMoreElements()) {Order each = (Order) e.nextElement();outstanding += each.getAmount();
}
System.out.println("name:" + _name);System.out.println("amount" + outstanding);
}
void printOwing() {
Enumeration e = _orders.elements();double outstanding = 0.0;
System.out.println("**************************");System.out.println("***** Customer Owes ******");System.out.println("**************************");
while (e.hasMoreElements()) {Order each = (Order) e.nextElement();outstanding += each.getAmount();
}
System.out.println("name:" + _name);System.out.println("amount" + outstanding);
}
void printBanner() {System.out.println("**************************");System.out.println("***** Customer Owes ******");System.out.println("**************************");
}
void printOwing() {Enumeration e = _orders.elements();double outstanding = 0.0;printBanner();while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();outstanding += each.getAmount();
}System.out.println("name:" + _name);System.out.println("amount" + outstanding);
}
void printOwing() {
Enumeration e = _orders.elements();double outstanding = 0.0;
System.out.println("**************************");System.out.println("***** Customer Owes ******");System.out.println("**************************");
while (e.hasMoreElements()) {Order each = (Order) e.nextElement();outstanding += each.getAmount();
}
System.out.println("name:" + _name);System.out.println("amount" + outstanding);
}
void printDetails(double outstanding) {System.out.println("name:" + _name);System.out.println("amount" + outstanding);
}
void printOwing() {Enumeration e = _orders.elements();double outstanding = 0.0;printBanner();while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();outstanding += each.getAmount();
}printDetails(outstanding);
}
void printOwing() {Enumeration e = _orders.elements();double outstanding = 0.0;printBanner();while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();outstanding += each.getAmount();
}printDetails(outstanding);
}
void printOwing() {Enumeration e = _orders.elements();double outstanding = 0.0;printBanner();while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();outstanding += each.getAmount();
}printDetails(outstanding);
}
double getOutstanding(double outstanding) {Enumeration e = _orders.elements();double result = 0.0;while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();result += each.getAmount();
}return result;
}
void printOwing() {printBanner();double outstanding = getOutstanding();printDetails(outstanding);
}
void printBanner() {System.out.println("**************************");System.out.println("***** Customer Owes ******");System.out.println("**************************");
}void printDetails(double outstanding) {
System.out.println("name:" + _name);System.out.println("amount" + outstanding);
}double getOutstanding(double outstanding)...
Replace Temp with QueryMotivation
The problem with temps is that they are temporary and local. Because they can be seen only in the context of the method in which they are used, temps tend to encourage longer methods, because that's the only way you can reach the temp. By replacing the temp with a query method, any method in the class can get at the information. That helps a lot in coming up with cleaner code for the class.
Replace Temp with Query often is a vital step before Extract Method. Local variables make it difficult to extract, so replace as many variables as you can with queries.
The straightforward cases of this refactoring are those in which temps are assigned only to once and those in which the expression that generates the assignment is free of side effects. Other cases are trickier but possible. You may need to use Split Temporary Variable or Separate Query from Modifier first to make things easier. If the temp is used to collect a result (such as summing over a loop), you need to copy some logic into the query method.
... double getPreco() {
int precoBase = _quantidade * _precoDoItem; double fatorDeDesconto; if (precoBase > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
... double getPreco() {
int precoBase = _quantidade * _precoDoItem; double fatorDeDesconto; if (precoBase > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
... double getPreco() {
final int precoBase = _quantidade * _precoDoItem; final double fatorDeDesconto; if (precoBase > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
... double getPreco() {
final int precoBase = precoBase(); final double fatorDeDesconto; if (precoBase > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
... double getPreco() {
final int precoBase = precoBase(); final double fatorDeDesconto; if (precoBase > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}…
private int precoBase() { return _quantidade * _precoDoItem;
}
... double getPreco() {
final int precoBase = precoBase(); final double fatorDeDesconto; if (precoBase > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
... double getPreco() {
final int precoBase = precoBase(); final double fatorDeDesconto; if (precoBase() > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
... double getPreco() {
final double fatorDeDesconto; if (precoBase() > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
... double getPreco() {
final double fatorDeDesconto; if (precoBase() > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
private double fatorDeDesconto() { if (precoBase() > 1000) return 0.95; else return 0.98;
}
... double getPreco() {
final double fatorDeDesconto = fatorDeDesconto(); if (precoBase() > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
... double getPreco() {
final double fatorDeDesconto = fatorDeDesconto(); return precoBase() * fatorDeDesconto;
}...
... double getPreco() {
return precoBase() * fatorDeDesconto();}private int precoBase() {
return _quantidade * _precoDoItem;}private double fatorDeDesconto() {
if (precoBase() > 1000) return 0.95; else return 0.98;
}...
... double getPreco() {
int precoBase = _quantidade * _precoDoItem; double fatorDeDesconto; if (precoBase > 1000) fatorDeDesconto = 0.95; else fatorDeDesconto = 0.98; return precoBase * fatorDeDesconto;
}...
double getPreco() { int pb = q * p; double fd; if (pb > 1000) fd = 0.95; else fd = 0.98;return pb * fd;
}
double getPreco() { int pb = q * p; double fd; if (pb > 1000) fd = 0.95; else fd = 0.98;return pb * fd;
}
“Quem foi o FDP que escreveu isso aqui?”
Replace Conditional with PolymorphismMotivation
One of the grandest sounding words in object jargon is polymorphism. The essence of polymorphsim is that it allows you to avoid writing an explicit conditional when you have objects whose behavior varies depending on their types.
As a result you find that switch statements that switch on type codes or if-then-else statements that switch on type strings are much less common in an object-oriented program.
Polymorphism gives you many advantages. The biggest gain occurs when this same set of conditions appears in many places in the program. If you want to add a new type, you have to find and update all the conditionals. But with subclasses you just create a new subclass and provide the appropriate methods. Clients of the class don't need to know about the subclasses, which reduces the dependencies in your system and makes it easier to update.
Replace Conditional with Polymorphism
double getSpeed() { switch (_type) {
case EUROPEAN: return getBaseSpeed();
case AFRICAN: return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;
case NORWEGIAN_BLUE: return (_isNailed) ? 0 : getBaseSpeed(_voltage);
} throw new RuntimeException ("Should be unreachable");
}
public class Employee {private EmployeeType _type;
int payAmount() {switch (getType()) {case EmployeeType.ENGINEER:
return _monthlySalary;case EmployeeType.SALESMAN:
return _monthlySalary + _commission;case EmployeeType.MANAGER:
return _monthlySalary + _bonus;default:
throw new RuntimeException("Incorrect Employee");}
}
int getType() {return _type.getTypeCode();
}
}
public class Employee {private EmployeeType _type;
int payAmount() {switch (getType()) {case EmployeeType.ENGINEER:
return _monthlySalary;case EmployeeType.SALESMAN:
return _monthlySalary + _commission;case EmployeeType.MANAGER:
return _monthlySalary + _bonus;default:
throw new RuntimeException("Incorrect Employee");}
}
int getType() {return _type.getTypeCode();
}
}
public class Employee {private EmployeeType _type;
int payAmount() {switch (getType()) {case EmployeeType.ENGINEER:
return _monthlySalary;case EmployeeType.SALESMAN:
return _monthlySalary + _commission;case EmployeeType.MANAGER:
return _monthlySalary + _bonus;default:
throw new RuntimeException("Incorrect Employee");}
}
int getType() {return _type.getTypeCode();
}
}
public class Employee {private EmployeeType _type;
int payAmount() {switch (getType()) {case EmployeeType.ENGINEER:
return _monthlySalary;case EmployeeType.SALESMAN:
return _monthlySalary + _commission;case EmployeeType.MANAGER:
return _monthlySalary + _bonus;default:
throw new RuntimeException("Incorrect Employee");}
}
int getType() {return _type.getTypeCode();
}
}
public int getMonthlySalary() {return _monthlySalary;
}public int getCommission() {
return _monthlySalary + _commission;}public int getBonus() {
return _monthlySalary + _bonus;}
public class Employee {private EmployeeType _type;
int payAmount() {switch (getType()) {case EmployeeType.ENGINEER:
return _monthlySalary;case EmployeeType.SALESMAN:
return _monthlySalary + _commission;case EmployeeType.MANAGER:
return _monthlySalary + _bonus;default:
throw new RuntimeException("Incorrect Employee");}
}
int getType() {return _type.getTypeCode();
}
}
public class EmployeeType ... int payAmount(Employee emp) {
switch (getTypeCode()) {case ENGINEER:
return emp.getMonthlySalary();case SALESMAN:
return emp.getMonthlySalary() + emp.getCommission();case MANAGER:
return emp.getMonthlySalary() + emp.getBonus();default:
throw new RuntimeException("Incorrect Employee");}
}…
public class EmployeeType ... int payAmount(Employee emp) {
switch (getTypeCode()) {case ENGINEER:
return emp.getMonthlySalary();case SALESMAN:
return emp.getMonthlySalary() + emp.getCommission();case MANAGER:
return emp.getMonthlySalary() + emp.getBonus();default:
throw new RuntimeException("Incorrect Employee");}
}…
public class Employee {…
int payAmount() {switch (getType()) {case EmployeeType.ENGINEER:
return _monthlySalary;case EmployeeType.SALESMAN:
return _monthlySalary + _commission;case EmployeeType.MANAGER:
return _monthlySalary + _bonus;default:
throw new RuntimeException("Incorrect Employee");}
}
…
}
public class Salesman extends EmployeeType{
int payAmount(Employee emp) {return emp.getMonthlySalary() + emp.getCommission();
}…
public class Manager extends EmployeeType {
int payAmount(Employee emp) {return emp.getMonthlySalary() + emp.getBonus();
}…
Decompose ConditionalMotivation
One of the most common areas of complexity in a program lies in complex conditional logic. As you write code to test conditions and to do various things depending on various conditions, you quickly end up with a pretty long method. Length of a method is in itself a factor that makes it harder to read, but conditions increase the difficulty. The problem usually lies in the fact that the code, both in the condition checks and in the actions, tells you what happens but can easily obscure why it happens.
As with any large block of code, you can make your intention clearer by decomposing it and replacing chunks of code with a method call named after the intention of that block of code. With conditions you can receive further benefit by doing this for the conditional part and each of the alternatives. This way you highlight the condition and make it clearly what you are branching on. You also highlight the reason for the branching.
if (date.before(SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;else
charge = quantity * _summerRate;
Decompose Conditional
if (date.before(SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;else
charge = quantity * _summerRate;
Decompose Conditional
if (date.before(SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;else
charge = quantity * _summerRate;
Decompose Conditional private boolean notSummer(Date date) {
return date.before (SUMMER_START) || date.after(SUMMER_END);
}
private double summerCharge(int quantity) { return quantity * _summerRate;
}
private double winterCharge(int quantity) { return quantity * _winterRate + _winterServiceCharge; }
if (notSummer(date))
charge = winterCharge(quantity);else
charge = summerCharge(quantity);
Decompose Conditional
private boolean notSummer(Date date) { return date.before (SUMMER_START) || date.after(SUMMER_END); }
private double summerCharge(int quantity) { return quantity * _summerRate;
}
private double winterCharge(int quantity) { return quantity * _winterRate + _winterServiceCharge; }
Decompose Conditional
The code "works", you know it works, but you need to refactor in order to put the code into a form you can understand
and work with in order to extend its functionality.
Tá funcionando então deixa!?
you write tests against the code, knowing that if the test fails, you wrote the test
wrong. Don't change the code until you are confident you have enough tests to let you know if one of your refactorings
broke something.
Tá funcionando então deixa!?
Testes de unidade automatizados!
… permita-se ser um profissional melhor…
Use teste automatizados!
@rponte
Projete para suas necessidades atuais, prever
o futuro pode não ser a melhor opção.
Se você não precisa, não faça!
Eu sei fazer isso chapa, você não! Bjus ;***
@OficialMaeDinah
https://twitter.com/OficialMaeDinah/status/58355091366871040
Simples é um adjetivo de dois gêneros e dois números, que descreve uma coisa que não é complicada, que não possui enfeites, ou que é clara, evidente ou natural. Também pode designar uma tarefa fácil de concretizar ou resolver (um problema de simples resolução).
Um projeto simples sempre leva menos tempo para terminar do que um problema complexo. Então, sempre faça a coisa mais simples que poderia funcionar em seguida. Se você encontrar algo que é complexo substituí-lo por algo simples. É sempre mais rápida e barata para substituir código complexo agora, antes de perder muito mais tempo com ela.
Um projeto simples sempre leva menos tempo para terminar do que um problema complexo. Então, sempre faça a coisa mais simples que poderia funcionar em seguida. Se você encontrar algo que é complexo substituí-lo por algo simples. É sempre mais rápida e barata para substituir código complexo agora, antes de perder muito mais tempo com ela.
Um projeto simples sempre leva menos tempo para terminar do que um problema complexo. Então, sempre faça a coisa mais simples que poderia funcionar em seguida. Se você encontrar algo que é complexo substituí-lo por algo simples. É sempre mais rápida e barata para substituir código complexo agora, antes de perder muito mais tempo com ela.
Manter as coisas o mais simples possível, desde que seja possível, nunca adicione funcionalidades antes de serem agendada. Cuidado, porém, manter um design simples é um trabalho difícil.
Manter as coisas o mais simples possível, desde que seja possível, nunca adicione funcionalidades antes de serem agendada. Cuidado, porém, manter um design simples é um trabalho difícil.
The Zen of Python
Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex.
Complex is better than complicated.
LinksThe principles of OOD ~ http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdfSimplicity ~ http://www.extremeprogramming.org/rules/simple.htmlRefactoring ~ http://refactoring.comRefactoring to Patterns ~ http://goo.gl/k8fGolBad Smells ~ http://sourcemaking.com/refactoring/bad-smells-in-codeCod Smells ~ http://c2.com/cgi/wiki?CodeSmellUsing Good Naming To Detect Bad Code ~ http://c2.com/cgi/wiki?UsingGoodNamingToDetectBadCodeFew Short Methods Per Class ~ http://c2.com/cgi/wiki?FewShortMethodsPerClassRefactoring Legacy Code ~ http://c2.com/cgi/wiki?RefactoringLegacyCode