Object
Calisthenics
#TDDMilano
Ferdinando Santacroce
@jesuswasrasta
“
2
The word calisthenics comes from
the ancient Greek words kalòs
(καλός), beauty, and sthénos
(σθένος), strength.
@jesuswasrasta #TDDMilano
“
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
@jesuswasrasta #TDDMilanobit.ly/ObjectCalisthenics
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 #TDDMilano
5
if(status==Status.DONE){
doSomething();
} else {//ELSE NOT ALLOWED!
doSomethingElse();
}
2. Don’t use ELSE keyword
@jesuswasrasta #TDDMilano
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 #TDDMilano
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 #TDDMilano
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 #TDDMilano
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 #TDDMilano
10
//Don't do this
String signOcr = "John Doe";
srep(signOcr);
//But this
String signatureFromOcr = "John Doe";
signReport(signatureFromOcr);
6. Don’t abbreviate
@jesuswasrasta #TDDMilano
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 #TDDMilano
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 #TDDMilano
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 #TDDMilano
14
@jesuswasrasta #TDDMilano
Time to get stronger!
Setup
Farkle Kata
2 pomodoros
Pair programming
TDD
Debriefing, Q&A
15
@jesuswasrasta #TDDMilano
github.com/jesuswasrasta/KataFarkle
LET’S GO!
16
@jesuswasrasta #TDDMilano
Dice, Turn, Throwing, Player, Score?
Too many objects?
Did rules conflicted, sometimes?
Does it felt weird or uncomfortable, maybe?
Debriefing
17
Summing up...
18
Encapsulation
19
Polimorphism
20
Naming
21
1. Only one level of indentation per method
Promotes method cohesiveness
Single Responsibility Principle (S.O.L.I.D.)
Extract method
Extract objects (collaborators)
@jesuswasrasta #TDDMilano
22
2. Don’t use ELSE keyword
Polymorphism
Behavioral Patterns
Strategy Pattern (set algorithm at runtime)
State Pattern (state machines)
@jesuswasrasta #TDDMilano
23
3. Wrap all primitives and strings
Primitive Obsession Anti-Pattern:
Using primitives to represent domain ideas
@jesuswasrasta #TDDMilano
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 #TDDMilano
25
5. One dot per line
Law of Demeter
Only talk with your immediate friends,
don’t talk to strangers
Fluent interfaces allowed
@jesuswasrasta #TDDMilano
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 #TDDMilano
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 #TDDMilano
28
8. No classes with more than two instance variables
High cohesion, low coupling
@jesuswasrasta #TDDMilano
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 #TDDMilano
Con il patrocinio di
31
Thanks!
Any questions?
You can find me at:
▫ @jesuswasrasta
▫ http://about.me/ferdinando.santacroce

Object Calisthenics - TDD Milano

  • 1.
  • 2.
    “ 2 The word calisthenicscomes from the ancient Greek words kalòs (καλός), beauty, and sthénos (σθένος), strength. @jesuswasrasta #TDDMilano
  • 3.
    “ 3 So, here’s anexercise that can help you to internalize principles of good object-oriented design and actually use them in real life. Jeff Bay @jesuswasrasta #TDDMilanobit.ly/ObjectCalisthenics
  • 4.
    4 class Board { publicString 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 #TDDMilano
  • 5.
    5 if(status==Status.DONE){ doSomething(); } else {//ELSENOT ALLOWED! doSomethingElse(); } 2. Don’t use ELSE keyword @jesuswasrasta #TDDMilano
  • 6.
    6 3. Wrap allprimitives 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 #TDDMilano
  • 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 #TDDMilano
  • 8.
    8 //Wrap collections inmeaningful 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 #TDDMilano
  • 9.
    9 public void update(){ //Onlyone 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 #TDDMilano
  • 10.
    10 //Don't do this StringsignOcr = "John Doe"; srep(signOcr); //But this String signatureFromOcr = "John Doe"; signReport(signatureFromOcr); 6. Don’t abbreviate @jesuswasrasta #TDDMilano
  • 11.
    11 7. Keep allentities 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 #TDDMilano
  • 12.
    12 8. No classeswith 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 #TDDMilano
  • 13.
    13 9. No getters/setters/properties //Youare 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 #TDDMilano
  • 14.
    14 @jesuswasrasta #TDDMilano Time toget stronger! Setup Farkle Kata 2 pomodoros Pair programming TDD Debriefing, Q&A
  • 15.
  • 16.
    16 @jesuswasrasta #TDDMilano Dice, Turn,Throwing, Player, Score? Too many objects? Did rules conflicted, sometimes? Does it felt weird or uncomfortable, maybe? Debriefing
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
    21 1. Only onelevel of indentation per method Promotes method cohesiveness Single Responsibility Principle (S.O.L.I.D.) Extract method Extract objects (collaborators) @jesuswasrasta #TDDMilano
  • 22.
    22 2. Don’t useELSE keyword Polymorphism Behavioral Patterns Strategy Pattern (set algorithm at runtime) State Pattern (state machines) @jesuswasrasta #TDDMilano
  • 23.
    23 3. Wrap allprimitives and strings Primitive Obsession Anti-Pattern: Using primitives to represent domain ideas @jesuswasrasta #TDDMilano
  • 24.
    24 4. First classcollections Encapsulation: Any class that contains a collection should contain no other member variables Give behaviors related to the collection a home @jesuswasrasta #TDDMilano
  • 25.
    25 5. One dotper line Law of Demeter Only talk with your immediate friends, don’t talk to strangers Fluent interfaces allowed @jesuswasrasta #TDDMilano
  • 26.
    26 6. Don’t abbreviate Naming “CleanCode” book, Robert C. Martin, Ch. 2 Meaningful, intention-revealing, pronounceable, searchable names Method’s name too long? Maybe it does too many things... @jesuswasrasta #TDDMilano
  • 27.
    27 7. Keep allentities 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 #TDDMilano
  • 28.
    28 8. No classeswith more than two instance variables High cohesion, low coupling @jesuswasrasta #TDDMilano
  • 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 #TDDMilano
  • 30.
  • 31.
    31 Thanks! Any questions? You canfind me at: ▫ @jesuswasrasta ▫ http://about.me/ferdinando.santacroce

Editor's Notes

  • #17 Non vi è sembrato strano, a tratti? (più lo fate e meno strano sembrerà) Che classi sono emerse? Le regole sono mai andate in contrasto? It’s only OOP, but I like it!
  • #19 2) Qual è il senso di queste regole? Capire meglio e fare uso di OOP 7 di 9: encapsulation
  • #20 2) Qual è il senso di queste regole? Capire meglio e fare uso di OOP Dont’ use else -> polimorfismo
  • #21 2) Qual è il senso di queste regole? Capire meglio e fare uso di OOP Naming strategy