T9 - Refactoring, Design Patterns & Anti-Patterns
T9 - Refactoring, Design Patterns & Anti-Patterns
22
Contents
• Previously on DP1…
• Refactoring
– “Movie rental” case study
– Refactoring steps
• Extract Method
• Move Method
• Remove OneToOne association
• Applying a design pattern
– Other Design Patterns of interest
3
Previously on DP1…
4
Our goal is to design applications
5
To help us on designing applications we (re-)use
6
We have mostly focused on architectural design
7
Now, we want to focus on the detailed design:
How should I organize my code?
In which method/class should I put my code?
8
The technical debt
9
Why is this important?
Why should I care about how the code is organized?
If it works… Isn’t it what matters?
The importance of a good code base
When adding a new feature you need to know how it fits in the
application and understand the existing code.
11
Cruft: The difference between the current code and how it would ideally be
12
https://www.martinfowler.com/bliki/TechnicalDebt.html
The cruft quadrant
Reckless Prudent
Deliberate “We don’t have time to “We must release now and
design” accept the consequences
(later)”
Inadvertent “What is a controller?” “Now we know how we
should have done it”
13
Technical debt
14
Technical debt
We should pay our technical debt while we evolve the code so
that it doesn’t get too high
Three questions now
Refactoring
18
How can we remove the cruft? REFACTORING!
19
“Movie Rental” Case Study
20
GET /customer/1/statement/
Our first solution
https://github.com/gii-is-DP1/react-petclinic/commit/b4026e98c21550f30cb8ed918311a76bb34d9c1f
22
23
New Feature!!!
• The users want to make changes to the way they classify movies, but they
haven't yet decided on the change they are going to make. These changes will
affect both the way renters are charged for movies and the way that frequent
renter points are calculated.
24
The program may not be
broken, but it does hurt.
It is making your life
more difficult because
you find it hard to make
the changes your users
want
25
When you find you have to add a feature to a program, and the
program's code is not structured in a convenient way to add the
feature, first refactor the program to make it easy to add the
feature, then add the feature.
In other words: Remove cruft!!
26
But before, the first step is adding tests
https://github.com/gii-is-DP1/react-
petclinic/commit/76b67f758fc497c2166
43587f8f47d441f65c065 27
What could we do first?
28
Extract Method
Code fragment that can be grouped
Converts the fragment in a method with a clear name
void printOwing() {
printBanner();
//print details
System.out.println ("name: " + _name);
System.out.println ("amount " + getOutstanding());
}
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
https://github.com/gii-is-DP1/react-
petclinic/commit/75643bfaa18db1d8a6358ecea583
a3cdffbbb0d6?diff=split
30
Refactoring changes the programs in small steps. If you make a
mistake, it is easy to find the bug.
What now?
Step 2: Rename variables in amountFor
https://github.com/gii-is-DP1/react-petclinic/commit/328d75357fc1d8664fab95a2b5a47dfbbf44779d 33
Is refactoring worth the effort?
34
What next?
35
Move Method
A method is used or will be used more by other classes than its own class
Create a new method with the code of the original method in the class that uses the
code more. Remove the original method or just delegate in the new one
or
Class2 Class2 Class2
+ unMetodo() + unMetodo()
Step 3: Move method amountFor to Rental as getCharge
https://github.com/gii-is-DP1/react- 37
petclinic/commit/24f183614357ca57637ace2e26c313fd14c712f0
State of classes after moving the method
38
Step 4: Removing OneToOne association (1)
https://github.com/gii-is-DP1/react-
petclinic/commit/f18ac9b46d239ff268b5c7fd40bb8e
2ba9f9c453 39
Step 4: Removing OneToOne association (2)
40
Step 4: Removing OneToOne association (3)
41
State of classes after moving the method
42
What else?
43
Step 5: Extract method getFrequentRenterPoints to Rental
44
https://github.com/gii-is-DP1/react-petclinic/commit/89107ee0d974b981ef602852150224eddb7362b0
State of classes
45
What else?
46
Step 6: Extract getTotalCharge and getTotalFrequentRenterPoints to
Customer (1)
https://github.com/gii-is-DP1/react-petclinic/commit/20b22f9eb013b2e9ce7f908141bbc181ce7bb485
47
Step 6: Extract getTotalCharge and getTotalFrequentRenterPoints to
Customer (and 2)
48
Wait a minute. What about the performance of this?
49
State of classes
50
What else?
51
We are moving what changes (the type of movie) to the class
that owns it (Movie)
52
Step 7: Move getCharge to Movie
https://github.com/gii-is-DP1/react-
petclinic/commit/5233a5220ca9398f68f888d859892
ac8499bff5c 53
Step 8: Move getFrequentRenterPoints to Movie
https://github.com/gii-is-DP1/react-petclinic/commit/2ae9fc60109bf232e13aa40abf6692410557f24d
54
State of classes
55
What else? Let’s look at the class Movie
We have several types of movie that have different ways of answering the same
question. This sounds like a job for subclasses. We can have three subclasses
of movie, each of which can have its own version of charge
56
Could this be a solution?
57
The solution is to use the state/strategy pattern
58
Step 9: Replace if-else with state/strategy for getCharge (1)
https://github.com/gii-is-DP1/react- 59
petclinic/commit/769a4b7d93a68de4e43a7027fb1470db3f5574fd
Step 9: Replace if-else with state/strategy for getCharge (2)
60
Step 9: Replace if-else with state/strategy for getCharged (and 3)
61
Step 10: Replace if-else with state/strategy for getFrequentRenterPoints (1)
https://github.com/gii-is-DP1/react-
petclinic/commit/f24ecf4a8827ccb9fefa0d182da0d0
41a63e2a4b
62
Step 10: Replace if-else with state/strategy for getFrequentRenterPoints (2)
63
The final result
64
The power of Design Patterns
66
Other design patterns of interest
• To create the game board, and or the Cards, and or the card
initial delivery .
• https://refactoring.guru/es/design-patterns/builder
68
Catalog: Moving features between objects
Extract Class
When one class does the work of two,
Create a class and move relevant fields and methods
Person PhoneNumber
Person
- name 1 - prefix
- prefix - name
- number
- number
+ getPhoneNumber() + getPhoneNumber()
+ getPhoneNumber()
Catalog: Organizing data
Employee Employee
- ENGINEER: int
- SALESMAN: int
- type: int
Engineer Salesman
Catalog: Simplifying method calls
Customer Customer
DateRange
- start : Date
- end : Date
And many more…
Check: https://refactoring.com/catalog/ and
https://refactoring.guru/refactoring/techniques
72
How can I identify what I should change?
73
Software antipatterns
74
What is Clean code?
75
The first source of cruft/dirt is… naming
We can go a bit further, and refactor this code creating a class for the cells of the game board:
76
https://www.commitstrip.com/en/2016/09/01/keep-it-simple-stupid/ 77
Another source of cruft/dirt is … Magic Numbers
78
Bad smells
Duplicated code / Copy&Paste programming
Bad smell: Same code structure in more than one place
Common solution: Move duplicated code into a new method, a new class, or just put
it in one class and call it from the other
This is a real-world
Example taken from
a student group
project (DP1 2020)
79
Bad smells
Comments
Bad smell: comments that are stating the obvious instead of providing value
// Add one to i.
i+=1;
80
More about comments
81
https://geekandpoke.typepad.com/geekandpoke/2008/07/good-comments.html 82
Antipattern: Boat Anchor
83
The worst type of Anchor: the required one
84
https://geekandpoke.typepad.com/geekandpoke/2009/07/
Bad smells
Long method
Bad smell: methods that are too long (more than one page rule)
Common solution: extract chunks of code from the long method into new methods
Continue… ➔
Continue… ➔
This is a real-world example taken from a student group project (DP1 2020) 85
Bad smells
Primitive obsession
Bad smell: overuse of primitive types (string, int…) instead of using value objects
(Money, Address…)
Common solution: Create value objects to ease the handling and validation of these
types
public Collection<Vuelo> horarioSemanalConFecha(int id, int dia,
int mes, int anyo){
...
}
This is a real-world example taken from a student group project (DP1 2020) 86
Bad smells
Message chains
Bad smell: a client asks one object for another object, which the client then asks for
yet another object… You find this as a long line of getThis methods
Common solution: extract part of the code of the method and move it to other object
in the chain
87
Wrapping up
88
Wrapping up
89
A+ Criteria published
90
Sneak peak on some A+ criteria: an easy one
@StatePattern
“Specification of the application of public class StatePatternApplication {
the design patterns in the project @StatePattern.State
interface SpeedLevel {
using the jpatterns library” void rotate(FanWallControl fanWallControl);}
@StatePattern.Context
static class FanWallControl {
<dependency> private SpeedLevel current;
<groupId>org.jpatterns</groupId>
<artifactId>jpatterns</artifactId> @StatePattern.ConcreteState
<version>0.0.1</version> static class Off implements SpeedLevel {
</dependency> public void rotate(FanWallControl fanWallControl) {
fanWallControl.set_state(new SpeedLevel1());
}
}
@StatePattern.ConcreteState
You have several examples available at static class SpeedLevel1 implements SpeedLevel {
the japatterns branch of the template public void rotate(FanWallControl fanWallControl)
fanWallControl.set_state(new SpeedLevel2());
{
@StatePattern.ConcreteState
https://github.com/gii-is-DP1/spring- static class SpeedLevel2 implements SpeedLevel {
public void rotate(FanWallControl fanWallControl) {
petclinic/tree/jpatterns/src/main/java/org/s fanWallControl.set_state(new SpeedLevel3());
pringframework/samples/petclinic/patterns }
}
@StatePattern.ConcreteState
static class SpeedLevel3 implements SpeedLevel {
public void rotate(FanWallControl fanWallControl) {
fanWallControl.set_state(new Off()); 91
} } }
Reminders
92
Sprint 2 Deadline
93
References
94
Bibliography
95
Bibliography
96
Bibliography
97
Disclaimer and Terms of Use
All material displayed on this presentation is for teaching and personal use only.
Many of the images that have been used in the presentation are Royalty Free images
taken from http://www.everystockphoto.com/. Other images have been sourced directly
from the Public domain, from where in most cases it is unclear whether copyright has
been explicitly claimed. Our intention is not to infringe any artist’s copyright, whether
written or visual. We do not claim ownership of any image that has been freely obtained
from the public domain. In the event that we have freely obtained an image or quotation
that has been placed in the public domain and in doing so have inadvertently used a
copyrighted image without the copyright holder’s express permission we ask that the
copyright holder writes to us directly, upon which we will contact the copyright holder to
request full written permission to use the quote or images.