Uma apresentação sobre Refactoring e Code Smells, abordando características do software em estado de apodrecimento, identificação de alguns code smells, e refactoring, e focado em melhoria contínua profissional em relação a engenharia de software.
13. Rigidez
Parece simples, mas difícil de mudar
Mudanças em cascata
Dependências entre módulos
Medo de correção de bugs
14. Fragilidade
Code and Fix
Manutenção Adventure
Desenvolvimento fora de controle
Credibilidade começa a cair
Terminei a feature!!!
Ei mah, tua feature quebrou!
15. Imobilidade
Não é possível reutilizar software
Muitas Dependências
Responsabilidades de mais
Risco alto para reutilizar
25. “Refactoring is a disciplined
technique for restructuring an
existing body of code, altering its
internal structure without
changing its external behavior.”
Martin Fowler
26. 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
31. Long Method
The 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.
[…]
32. 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.
[…]
33. public int fechaFolha(String folha) {
// imprime a folha adicionando bla bla bla
try {
FileReader arq = new FileReader(folha);
BufferedReader lerArq = new BufferedReader(arq);
String linha = lerArq.readLine();
while (linha != null) {
System.out.printf("%sn", 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 é especial
if (linha.contains("ARS0010304")) {
this.folhaEspecial = true;
}
35. 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("%sn", linha);
this.linha += linha;
linha = lerArq.readLine();
}...
I throw away
commented code
36. 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.
40. 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.
43. 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.
44. 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.
46. Long Parameter List
public Object method(int var,int var1,int
var2,int var3,int var4,int var5,int var6,int
var7) {
...
}
47. 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.
[…]
51. Duplicated Code
Large Class
Method
Shotgun Surgery Long Parameter List
Refused Bequest
BBAADD SSMMDatEEa ClLaLssLLSS
Comments Lazy Class
Divergent Change
Parallel Inheritance Hierarchies
Switch Statements
Primitive Obsession
Data Clumps
Feature
Envy Incomplete Library Class
Alternative Classes with Different Interfaces
Inappropriate
Intimacy
Middle Man
Message Chains
Temporary Field
Speculative Generality
53. Extract Method
Motivation
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.
54. 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);
}
55. 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);
}
64. Replace Temp with Query
Motivation
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.
80. 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?”
81. Replace Conditional with Polymorphism
Motivation
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.
82. 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");
}
85. 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();
}
86. 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();
}
87. 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();
}
88. 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;
}
89. 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();
}
90. 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");
}
}
…
91. 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");
}
}
…
92. 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");
}
}
…
}
94. 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();
}
…
95. Decompose Conditional
Motivation
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.
101. Tá funcionando então deixa!?
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.
102. 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.
104. Não tenha medo
de mudar, isto só
perigoso quando
você não tem
testes de unidade
automatizados!
105. … permita-se ser um
profissional melhor…
Use teste automatizados!
@rponte
Testes de unidade
automatizados!
106. 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!
@OficialMaeDinah
Eu sei fazer isso chapa,
você não! Bjus ;***
120. 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).
121. 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.
122. 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.
123. 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.
124. 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.
125. 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.
126. Simplicidade: a arte de maximizar a quantidade
de trabalho que não precisou ser feito.
127. The Zen of Python
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
129. Links
The principles of OOD ~ http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf
Simplicity ~ http://www.extremeprogramming.org/rules/simple.html
Refactoring ~ http://refactoring.com
Refactoring to Patterns ~ http://goo.gl/k8fGol
Bad Smells ~ http://sourcemaking.com/refactoring/bad-smells-in-code
Cod Smells ~ http://c2.com/cgi/wiki?CodeSmell
Using Good Naming To Detect Bad Code ~ http://c2.com/cgi/wiki?UsingGoodNamingToDetectBadCode
Few Short Methods Per Class ~ http://c2.com/cgi/wiki?FewShortMethodsPerClass
Refactoring Legacy Code ~ http://c2.com/cgi/wiki?RefactoringLegacyCode