The document discusses object calisthenics, which are exercises that help internalize good object-oriented design principles. It outlines 9 rules for object calisthenics, including having only one level of indentation per method, not using else keywords, wrapping primitives in objects, using first class collections, following the law of Demeter, avoiding abbreviations, keeping all entities small, limiting classes to two properties, and avoiding getters and setters. The goals are to promote cohesion, encapsulation, polymorphism, and other solid principles of object-oriented design. Examples are provided to illustrate how to refactor code to follow the rules.
2. “
2
The word calisthenics comes from
the ancient Greek words kalòs
(καλός), beauty, and sthénos
(σθένος), strength.
@jesuswasrasta #AgileVenturePrato2018
3. “
3
So, here’s an exercise that can help you to
internalize principles of good object-oriented
design and actually use them in real life.
Jeff Bay
bit.ly/ObjectCalisthenics
@jesuswasrasta #AgileVenturePrato2018
4. 4
class Board {
public String print() {
StringBuilder buffer = new StringBuilder();
//Level 0
for (int i = 0; i < 10; i++) {
//Level 1: OK
for (int j = 0; j < 10; j++) {
//Level 2: NOT ALLOWED!
buffer.append(data[i][j]);
}
buffer.append("n");
}
return buffer.toString();
}
}
1. Only one level of indentation per method
@jesuswasrasta #AgileVenturePrato2018
6. 6
3. Wrap all primitives and strings
class ZipCode{
String regex = "^[0-9]{5}(?:-[0-9]{4})?$";
Pattern pattern = Pattern.compile(regex);
String value;
public ZipCode(String value) {
this.value = value;
}
public boolean isWellFormed(){
return pattern.matcher(value).matches();
}
}
//Don't use primitives!
String regex = "^[0-9]{5}(?:-[0-9]{4})?$";
Pattern pattern = Pattern.compile(regex);
String zip = "12345";
if (pattern.matcher(value).matches()){
//...
}
//Use objects, we are doing OOP, after
all..
ZipCode zipCode = new ZipCode("12345");
if(zipCode.isWellFormed()){
//...
}
@jesuswasrasta #AgileVenturePrato2018
7. 7
//Don't use collections, directly
List<String> players = new ArrayList<>();
String player = "John";
if(!(players.contains(player) || players.size() == 5)){
players.add(player);
}
4. First class collections (1)
@jesuswasrasta #AgileVenturePrato2018
8. 8
//Wrap collections in meaningful objects
Team team = new Team();
team.add(player);
//...
class Team {
List<String> players = new ArrayList<>();
final int TEAM_SIZE = 5;
public void add(String player){
if(players.contains(player)){
throw new DuplicatePlayer("Player " + player + " already present");
}
if(players.size() == TEAM_SIZE){
throw new TeamFull("Team full, max " + TEAM_SIZE + " players");
}
players.add(player);
}
4. First class collections (2)
@jesuswasrasta #AgileVenturePrato2018
9. 9
public void update(){
//Only one dot per line
context.getSession().update();
//That doesn't means this...
Session session = context.getSession();
session.update();
//But this...
context.update();
}
5. One dot per line
@jesuswasrasta #AgileVenturePrato2018
10. 10
//Don't do this
String signOcr = "John Doe";
srep(signOcr);
//But this
String signatureFromOcr = "John Doe";
signReport(signatureFromOcr);
6. Don’t abbreviate
@jesuswasrasta #AgileVenturePrato2018
11. 11
7. Keep all entities small
//But small and cohesive ones like these!
class Player{
Identity identity;
Address address;
public Player(Identity identity){
this.identity = identity;
}
public changeAddress(Address address){
this.address = address;
}
}
//Don’t create this kind of entities...
class Player{
String firstName;
String lastName;
String nickName;
String city;
String street;
String streetNumber;
String zipCode;
public void setFirstName(String firstName){
this.firstName = firstName;
}
//...
//And so on, tons of getters and setter
//...
}
class Identity{
String firstName;
String lastName;
String nickName;
}
class Address{
String city;
String street;
String streetNumber;
String zipCode;
}
@jesuswasrasta #AgileVenturePrato2018
12. 12
8. No classes with more than two instance variables
//But you have to do this!
class Identity{
FamilyName familyName;
GivenName givenName;
}
//So, you can’t do this...
class Identity{
String firstName;
String lastName;
String nickName;
} class FamilyName{
String firstName;
String lastName;
}
class GivenName{
String nickName;
}
@jesuswasrasta #AgileVenturePrato2018
13. 13
9. No getters/setters/properties
//You are not allowed to do this...
class Player{
String firstName;
//...
public String zipCode;
public void getFirstName(String firstName){
return this.firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
}
//And so on...
} @jesuswasrasta #AgileVenturePrato2018
14. 14
Time to get stronger!
Setup
Tennis Game Kata
3-4 pomodoros
Pair programming
TDD
Debriefing, Q&A
@jesuswasrasta #AgileVenturePrato2018
21. 21
1. Only one level of indentation per method
Promotes method cohesi-veness
Single Responsibility Principle (S.O.L.I.D.)
Extract method
Extract objects (collaborators)
@jesuswasrasta #AgileVenturePrato2018
22. 22
2. Don’t use ELSE keyword
Polymorphism
Behavioral Patterns
Strategy Pattern (set algorithm at runtime)
State Pattern (state machines)
@jesuswasrasta #AgileVenturePrato2018
23. 23
3. Wrap all primitives and strings
Primitive Obsession Anti-Pattern:
Using primitives to represent domain ideas
@jesuswasrasta #AgileVenturePrato2018
24. 24
4. First class collections
Encapsulation:
Any class that contains a collection
should contain no other member variables
Give behaviors related to the collection a home
@jesuswasrasta #AgileVenturePrato2018
25. 25
5. One dot per line
Law of Demeter
Only talk with your immediate friends,
don’t talk to strangers
Fluent interfaces allowed
@jesuswasrasta #AgileVenturePrato2018
26. 26
6. Don’t abbreviate
Naming
“Clean Code” book, Robert C. Martin, Ch. 2
Meaningful, intention-revealing,
pronounceable, searchable names
Method’s name too long?
Maybe it does too many things...
@jesuswasrasta #AgileVenturePrato2018
27. 27
7. Keep all entities small
Single Responsibility Principle (again...)
Class with 50 l.o.c, methods with 5
The “class that fits in the monitor” rule
There’s no black & white, it’s all about trade-offs
@jesuswasrasta #AgileVenturePrato2018
28. 28
8. No classes with more than two instance variables
High cohesion, low coupling
@jesuswasrasta #AgileVenturePrato2018
29. 29
9. No getters/setters/properties
Tell, don’t ask
Feature envy code-smell
(extensive use of another class)
They violates Open/Closed Principle (S.O.L.I.D.)
Anemic Domain anti-pattern
@jesuswasrasta #AgileVenturePrato2018