0% found this document useful (0 votes)
119 views10 pages

11 Steps To Better Software Design Today.: Object Calisthenics

The document discusses an exercise called "Object Calisthenics" which consists of 11 steps to improve software design skills through strict adherence to coding standards. The exercise challenges developers to complete a simple project following 12 rules, such as limiting methods to a single level of indentation and avoiding static methods. By constraining design in this way, developers can learn to think differently about how to structure code in an object-oriented way and improve skills like encapsulation and separation of concerns. Completing the exercise according to the provided rules can help internalize good object-oriented principles even if some rules may not always apply in every project.

Uploaded by

Michael Ulguim
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
119 views10 pages

11 Steps To Better Software Design Today.: Object Calisthenics

The document discusses an exercise called "Object Calisthenics" which consists of 11 steps to improve software design skills through strict adherence to coding standards. The exercise challenges developers to complete a simple project following 12 rules, such as limiting methods to a single level of indentation and avoiding static methods. By constraining design in this way, developers can learn to think differently about how to structure code in an object-oriented way and improve skills like encapsulation and separation of concerns. Completing the exercise according to the provided rules can help internalize good object-oriented principles even if some rules may not always apply in every project.

Uploaded by

Michael Ulguim
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

Object Calisthenics

11 steps to better software design today.


Weve all seen poorly written code thats hard to understand, test, and maintain. Object-oriented
programming promised to save us from our old procedural code, allowing us to write software
incrementally, reusing as we go along. But sometimes it seems like were just chasing down the
same old comple, coupled designs in !ava that we had in ".
#ood object-oriented design is hard to learn. $ransitioning from procedural development to
object-oriented design re%uires a major shift in thinking that is more difficult than it seems.
&any developers assume theyre doing a good job with OO design, when in reality theyre
unconsciously stuck in old habits that are hard to break. 't doesnt help that many eamples and
best practices (even )uns code in the !*+, encourage poor OO design in the name of
performance or simple weight of history.
$he core concepts behind good design are well understood. -lan )halloway has suggested that
seven code %ualities matter. cohesion, loose coupling, no redundancy, encapsulation, testability,
readability, and focus. /et its hard to put those concepts into practice. 'ts one thing to
understand that encapsulation means hiding data, implementation, type, design, or construction.
'ts another thing altogether to design code that implements encapsulation well. )o heres an
eercise that can help you to internali0e principles of good object-oriented design and actually
use them in real life.
The Challenge
*o a simple project using far stricter coding standards than youve ever used in your life.
Below, youll find 12 3rules of thumb4 that will help push your code into good object-oriented
shape.
By suspending disbelief, and rigidly applying these rules on a small, 1555 line project, youll
start to see a significantly different approach to designing software. Once youve written 1555
lines of code, the eercise is done, and you can rela and go back to using these 12 rules as
guidelines.
$his is a hard eercise, especially because many of these rules are not universally applicable.
$he fact is, sometimes classes are a little more than 65 lines. But theres great value in thinking
about what would have to happen to move those responsibilities into real, first-class-objects of
their own. 'ts developing this type of thinking thats the real value of the eercise. )o stretch
the limits of what you imagine is possible, and see whether you start thinking about your code in
a new way.
The Rules
1. *ont abbreviate
2. One level of indentation per method
7. *ont use the 89)8 keyword
:. ;o static methods other than factory methods
6. +eep all entities small
<. One level of indentation per method
=. One dot per line
>. Wrap all primitives
?. @irst class collections
15. One dot per line
11. *ont abbreviate
12. +eep all entities small
17. @irst class collections
1:. ;o classes with more than 2 instance variables.
16.
1<. ;o getAset methods
1=. -ll classes must have state
1>. ;o static methods other than factory methods
Rule 1: One level of indentation per method
8ver stare at a big old method wondering where to startB - giant method lacks cohesiveness.
One guideline is to limit method length to 6 lines, but that kind of transition can be daunting if
your code is littered with 655-line monsters. 'nstead, try to ensure that each method does eactly
one thing C one control structure, or one block of statements, per method. 'f youve got nested
control structures in a method, youre working at multiple levels of abstraction, and that means
youre doing more than one thing.
-s you work with methods that do DeactlyD one thing, epressed within classes doing eactly
one thing, your code begins to change. -s each unit in your application becomes smaller, your
level of re-use will start to rise eponentially. 't can be difficult to spot opportunities for reuse
within a method that has five responsibilities and is implemented in 155 lines. - three-line
method that manages the state of a single object in a given contet is usable in many different
contets.
Ese the Extract Method feature of your '*8 to pull out behaviors until your methods only have
one level of indentation, like this. FeampleG
class Board H
...
)tring board(, H
)tringBuffer buf I new )tringBuffer(,J
for(int i I 5J i F 15J iKK, H
for(int j I 5J j F 15J jKK,
buf.append(dataLiMLjM,J
buf.append(3Nn4,J
O
return buf.to)tring(,J
O
O
"lass Board H
...
)tring board(, H
)tringBuffer buf I new )tringBuffer(,J
collectPows(buf,J
Peturn buf.to)tring(,J
O
Qoid collectPows()tringBuffer buf, H
@or(int ' I 5J ' F 15J iKK,
collectPow(buf, i,J
O
Qoid collectPow()tringBuffer buf, int row, H
@or(int ' I 5J ' F 15J iKK,
Buf.append(dataLrowMLiM,J
buf.append(3Nn4,J
O
O
;otice that another effect has occurred with this refactoring. 8ach individual method has become
virtually trivial to match its implementation to its name. *etermining the eistence of bugs in
these much smaller snippets is fre%uently much easier.
Rere at the end of the first rule, we should also point out that the more you practice applying the
rules, the more the advantages come to fruition. /our first attempts to decompose problems in
the style presented here will feel awkward and likely lead to little gain you can perceive. $here is
a skill to the application of the rules C this is the art of the programmer raised to another level.
Rule 2: Dont use the E!E "e#$ord
8very programmer understands the ifAelse construct. 'ts built into nearly every programming
language, and simple conditional logic is easy for anyone to understand. ;early every
programmer has seen a nasty nested conditional thats impossible to follow, or a case statement
that goes on for pages. 8ven worse, it is all too easy to simply add another branch to an eisting
conditional rather than factoring to a better solution. "onditionals are also a fre%uent source of
duplication. )tatus flags and state of residence are two eamples which fre%uently lead to this
kind of trouble.
if (status II *O;8, H
do)omething(,J
O else H
S
Object-oriented languages give us a powerful tool, polymorphism, for handling comple
conditional cases. *esigns that use polymorphism can be easier to read and maintain, and
epress their intent more clearly. But its not always easy to make the transition, especially
when we have 89)8 in our back pocket. )o as part of this eercise, youre not allowed to use
89)8. $ry the ;ull Object patternJ it may help in some situations. $here are other tools that can
help you rid yourself of the else as well. )ee how many alternatives you can come up with.
Rule %: &rap all primitives and !trings
'n the !ava language, int is a primitive, not a real object, so it obeys different rules than objects.
't is used with a synta that isnt object-oriented. &ore importantly, an int on its own is just a
scalar, so it has no meaning. When a method takes an int as a parameter, the method name
needs to do all of the work of epressing the intent. 'f the same method takes an Rour as a
parameter, its much easier to see whats going on. )mall objects like this can make programs
more maintainable, since it isnt possible to pass a /ear to a method that takes an Rour
parameter. With a primitive variable the compiler cant help you write semantically correct
programs. With an object, even a small one, you are giving both the compiler and the
programmer additional info about what the value is and why it is being used.
)mall objects like Rour or &oney also give us an obvious place to put behavior that would
otherwise have been littered around other classes. $his becomes especially true when you apply
the Rule ', and only the small object can access the value.
Rule (: )irst class collections
-pplication of this rule is simple. any class that contains a collection should contain no other
member variables. 8ach collection gets wrapped in its own class, so now behaviors related to
the collection have a home. /ou may find that filters become a part of this new class. -lso,
your new class can handle activities like joining two groups together or applying a rule to each
element of the group.
Rule *(: One dot per line
)ometimes its hard to know which object should take responsibility for an activity. 'f you start
looking for lines of code with multiple dots, youll start to find many misplaced responsibilities.
'f youve got more than one dot on any given line of code, the activity is happening in the wrong
place. &aybe your object is dealing with two other objects at once. 'f this is the case, your
object is a middlemanJ it knows too much about too many people. "onsider moving the activity
into one of the other objects.
'f all those dots are connected, your object is digging deeply into another object. $hese multiple
dots indicate that youre violating encapsulation. $ry asking that object to do something for you,
rather than poking around its insides. - major part of encapsulation is not reaching across class
boundaries into types that you shouldnt know about.
$he 9aw of *emeter (3Only talk to your friends4, is a good place to start, but think about it this
way. /ou can play with your toys, toys that you make, and toys that someone gives you. /ou
dont ever, DeverD play with your toys toys.
class Board H
...

class Tiece H
...
)tring representationJ
O
class 9ocation H
...
Tiece currentJ
O
)tring boardPepresentation(, H
)tringBuffer buf I new )tringBuffer(,J
for(9ocation l . s%uares(,,
buf.append(l.current.representation.substring(5, 1,,J
return buf.to)tring(,J
O
O
class Board H
...

class Tiece H
...
private )tring representationJ
)tring character(, H
return representation.substring(5, 1,J
O

void add$o()tringBuffer buf, H
buf.append(character(,,J
O
O
class 9ocation H
...
private Tiece currentJ
void add$o()tringBuffer buf, H
current.add$o(buf,J
O
O
)tring boardPepresentation(, H
)tringBuffer buf I new )tringBuffer(,J
for(9ocation l . s%uares(,,
l.add$o(buf,J
return buf.to)tring(,J
O
O
class Board H
...

class Tiece H
...
)tring representationJ
O
class 9ocation H
...
Tiece currentJ
O
)tring boardPepresentation(, H
)tringBuffer buf I new )tringBuffer(,J
for(9ocation l . s%uares(,,
buf.append(l.current.representation.substring(5, 1,,J
return buf.to)tring(,J
O
O
class Board H
...

class Tiece H
...
private )tring representationJ
)tring character(, H
return representation.substring(5, 1,J
O

void add$o()tringBuffer buf, H
buf.append(character(,,J
O
O
class 9ocation H
...
private Tiece currentJ
void add$o()tringBuffer buf, H
current.add$o(buf,J
O
O
)tring boardPepresentation(, H
)tringBuffer buf I new )tringBuffer(,J
for(9ocation l . s%uares(,,
l.add$o(buf,J
return buf.to)tring(,J
O
O
;ote that in this eample the algorithmUs implementation details are more diffuse, which can
make it a little harder to understand at a glance as a whole, however, you have created a named
method for the pieceUs transformation into a character. $his is a method with a strong cohesive
name and job, and is %uite likely to be reused - the odds of Vrepresentation.substring(5, 1,V being
repeated in other parts of the program has now been reduced dramatically.
Rule +*: Dont abbreviate
'ts often tempting to abbreviate in the names of classes, methods, or variables. Pesist the
temptation C abbreviations can be confusing, and they tend to hide larger problems.
$hink about why you want to abbreviate. 's it because youre typing the same word over and
over againB 'f thats the case, perhaps your construct method is used too heavily and you are
missing opportunities to remove duplication. 's it because your method names are getting longB
$his might be a sign of a misplaced responsibility, or a missing class.
$ry to keep class and method names to 1-2 words, and a$he etension of this rule re%uires that
names are never more than 2 words long. -void names that duplicate the contet. 'f the class is
an Order, the method doesnt need to be called shipOrder(,. )imply name the method ship(ship(,
so that clients call simply see order.ship(, C a simple and clear representation of whats going on.
Rule ,+: -eep all entities small
$his means no class over 65 lines and no package over 15 files..
;o class over 65 lines
W ;o package over 15 files
"lasses over 65 lines are generally doingusually do more than one thing, which makes them
harder to understand and harder to reuse. 65-line classes have the added benefit of being visible
on one screen without scrolling, which makes them easier to grasp %uickly.
Whats challenging about creating such small classes is that there are often groups of behaviors
that make logical sense together. $his is where we need to leverage packages. -s your classes
become s;ow that your classes are smaller and have fewer responsibilities, and as you limit
package si0ewe can start to really leverage packages. -s you limit package si0e, youll start to
see that packages represent clusters of related classes that work together to achieve a goal.
Tackages, like classes, should be cohesive and have a purpose. +eeping those packages small
forces them to have a real identity.
Rule ,: )irst class collections
-pplication of this rule is simple. any class that contains a collection should contain no other
member variables. Behavior related to the collection suddenly has a home. $his rule has some
interesting side effects C you may find that filters become a part of the interface to these classes.
$he new class can handle activities like joining two groups together or applying a rule to each
element of the group.
Rule .: /o classes $ith more than t$o instance variables
&ost classes should simply be responsible for handling a single state variable, but there are a few
that will re%uire two. -dding a new instance variable to a class immediately decreases the
cohesion of that class. 'n general, while programming under these rules, youll find that there are
two kinds of classes, those that maintain the state of a single instance variable, and those that
coordinate two separate variables. 'n general, dont mi the two kinds of responsibilities.
$he discerning reader might have noticed that rules 7 and = can be considered to be isomorphic.
'n a more general sense, there are few cases where a cohesive single job description can be
created for a class with many instance variables. -s an eample of the kind of dissection we are
asking you to engage in.
class ;ame H
)tring firstJ
)tring middleJ
)tring lastJ
O
would be decomposed into two classes thus.
class ;ame H
)urname familyJ
#iven;ames givenJ
O
class )urname H
)tring familyJ
O
class #iven;ames H
9istF)tringG namesJ
O
;ote that in thinking about how to do the decomposition, the opportunity to separate the
concerns of a family name (used for many legal entity restrictions, could be separated from an
essentially different kind of name. $he #iven;ame object here contains a list of names, allowing
the new model to absorb people with first, middle, and other given names. @re%uently,
decomposition of instance variables leads to an understanding of commonality of several related
instance variables. )ometimes several related instance variables actually have a related life in a
first class collection.
'ndeed, it is the authorsU eperience that decomposing objects from a set of attributes into a
heirarchy of collaborating objects, leads much more directly to an effective object model. Trior
to understanding this rule, we spent many hours trying to follow data flows through large
objects. 't was possible to twee0e out an object model, but it was a painstaking process to
understand the related groups of behavior and see the result. 'n contrast, the recursive application
of this rule has lead to very %uick decomposition of comple large objects into much simpler
models. Behavior naturally follows the instance variables into the appropriate place.
Rule 0: /o getters1setters1properties
$he last sentence of the previous rule leads almost directly to this rule. 'f your objects are now
encapsulating the appropriate set of instance variables but the design is still awkward, it is time
to eamine some more direct violations of encapsulation. $he behavior will not follow the
instance variable if it can simply ask for the value in its current location. $he idea behind strong
encapsulation boundaries is to force programmers working on the code after you leave it to Dlook
forD and DplaceD behavior into a single place in the object model. $his has many beneficial
downstream effects, such as a dramatic reduction in duplication errors and a better locali0ation of
changes to implement new features.
-nother way this rule is commonly stated is 3$ell, dont ask4.
Rule 12: 3ll classes must have state4 no static methods4 no utilit# classes
$his rule is perhaps the hardest to eplain, and it is not nearly as clear-cut as the previous rules.
't is a catch-all intended to catch a class of errors in design that proliferate, particularly in
modern java code, and as mentioned earlier C in )uns !*+ -T's themselves. Etility classes that
simply operate on other objects have no 3identity4, no raison detre. $hey ask objects for data,
manipulate the data on behalf of others, and in general, are fre%uent violators of encapsulation.
Conclusion
> of these 15 rules are simply ways to visuali0e and implement the holy grail of object oriented
programming C encapsulation of data. 'n addition, another drives the appropriate use of
polymorphism Lnot using else and minimi0ing all conditional logicM, and another is a naming
strategy that encourages concise and straightforward naming standards C without inconsistently
applied and hard to pronounce abbreviations. $he entire thrust is to craft code that has no
duplication in code or idea. "ode which concisely epresses simple and elegant abstractions for
the incidental compleity we deal with all day long.
'n the long run, you will inevitably find that these rules contradict each other in some situations,
or the application of the rules leads to degenerate results. @or the purpose of the eercise,
however, spend 25 hours and 1555 lines writing code that conforms 155X to these rules. /ou
will find yourself having to break yourself of old habits and change rules that you may have
lived with for your whole programming life. 8ach of the rules has been chosen such that if you
follow it you will encounter situations that would typically have an easy answer that is not
available to you.
@ollowing these rules with discipline will force you to come up with the harder answers that lead
to a much richer understanding of object oriented programming. 'f you write a thousand lines
that follow all these rules you will find that you have created something completely different
than you epected. @ollow the rules, and see where you end up. 'f it isnt comfortable, back off
and see what you can leverage that is comfortable. /ou might find that if you keep working at it,
you see code you are writing conform to these rules without any conscious effort on your part.

You might also like