Object Calisthenics

WIP

I love learning new things, often the way we code solutions has a cool name that we actually did not know about. Object Calisthenics is such a case (for me anyway) - I was amazed to read / watch engineers build and talk about code and draw similarities to the way I approach code solutions in the real world.

Calisthenics are exercises that don’t rely on anything but a person’s own body weight.

Interesting that this name was chosen :)

As with all patterns and principles, they are not rules but mostly rather guidelines. The actual implementation is always tailored to the problem and each engineers solution will be built from their perspective.

So what are object calisthenics? Well they are a set of rules written by Jeff Bay (The Thoughtworks Anthology) and consist of the ‘rules’ defined below. I’m always sceptical of ‘rules’ for programming but feel its import to try understand other peoples perspective - we can ALL always learn something :)

  1. One level of indentation per method
  2. Dont use the ELSE keyword
  3. Wrap all primitives and strings
  4. First class collections
  5. One dot per line
  6. Dont abbreviate
  7. Keep all entities small
  8. No class with more tan 2 instance variables
  9. No getter/setter properties

Most of the content below is based on the amazing work by Nick Chapsas. What a legend!

Examples below were simplified for readability.

One level of indentation per method

This is the level of nesting in methods. The value of following this is debatable but I do feel it will highlight where logic could possibly be injected and follow Single Responsibility Principle (SRP)

You can extract whole code blocks out to be their own private methods and where these grow or can be grouped extract them out to their own classes.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void AskForDrink()
{
...
if (drink.Equals("beer")) // this is level 1 nest
{
_outputProvider("How old are you?"); // this is level 2 nest
if (!int.TryParse(_outProvider(), out var age))
{
_outputProvider("Could not parse age");
}
else
{
if (age >= 18)
{
_outputProvider("Here's your cold beer.");
}
else
{
_outputProvider("Sorry mate you are not old enough.");
}
}
}
else if ...
}

Becomes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public void AskForDrink()
{
...
if (drink.Equals("beer")) // this is level 1 nest
{
HandleBeer();
}
else if ...
}

private void HandleBeer()
{
_outputProvider("How old are you?"); // this is level 2 nest
if (!int.TryParse(_outProvider(), out var age))
{
_outputProvider("Could not parse age");
}
else
{
if (age >= 18) // This could also then be extracted out
{
_outputProvider("Here's your cold beer.");
}
else
{
_outputProvider("Sorry mate you are not old enough.");
}
}
}

Dont use the ELSE keyword

This this can significantly increase your codes readability, its not that we really remove the else but rather making use of language features to return when the else would be evaluated.

This is not going to work for MVC Views or Blazer but it can work very well for your class files (code behind)

Using else conditions can also introduce smells like violation of Open/Closed Principle (OCP), so everytime we need a new condition the else if is used which is a modification.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void HandleBeer()
{
_outputProvider("How old are you?"); // this is level 2 nest
if (!int.TryParse(_outProvider(), out var age))
{
_outputProvider("Could not parse age");
}
else
{
if (age >= 18) // This could also then be extracted out
{
_outputProvider("Here's your cold beer.");
}
else
{
_outputProvider("Sorry mate you are not old enough.");
}
}
}

becomes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void HandleBeer()
{
_outputProvider("How old are you?");
if (!int.TryParse(_outProvider(), out var age))
{
_outputProvider("Could not parse age");
return;
}

if (age >= 18) // This could also then be extracted out
{
_outputProvider("Here's your cold beer.");
return;
}

_outputProvider("Sorry mate you are not old enough.");
}

Simple checks like these can also be represented with a ternary operator but that depends on your team ways of working - its always best to keep the code copy the same thoughout the code base.

1
2
3
_outputProvider(age >= 18)
? "Here's your cold beer."
: "Sorry mate you are not old enough."

Nick also extracted the whole age check out into its own method HandleBeerAgeCheck(int age) but I felt the above was fine.

Use a switch

If you are using a modern IDE like Visual Studio 2022 it will make these suggestions for you. This is not explicitly removing else, its just a different type of check which most engineers would find acceptable.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void AskForDrink()
{
...
if (drink.Equals("beer"))
{
HandleBeer();
}
else if (drink.Equals("juice"))
{
HandleJuice();
}
else
{
HandleUnavailableDrink(drink);
}
}

becomes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void AskForDrink()
{
...
switch(drink)
{
case "beer":
HandleBeer();
break;
case "juice":
HandleJuice();
break;
default:
HandleUnavailableDrink();
break;
}
}

Wrap all primitives and strings

A primitive type is a built in type, see example C# types. Note that in some languages, strings are not primitives. (They are in C#)

First class collections

https://youtu.be/gyrSiY4SHxI?t=402

References