In a previous article I wrote about the first rule of East-oriented Code.
Here again are the rules I set forth in my presentation at RubyConf:
- Always return self
- Objects may query themselves
- Factories are exempt
- Break the rules sparingly
The second rule, that "Objects may query themselves", allows the design of objects to work with their own attributes.
When we design our systems of interacting objects we can use the Tell, Don't Ask approach to limit the decisions in the code to objects which are responsible for the data used to make them.
The Tell, Don't Ask article begins by quoting Alec Sharp:
Procedural code gets information then makes decisions. Object-oriented code tells objects to do things.
In order for objects to do things, they may need to ask questions about their own data. Though the first rule of East-oriented Code says that you should return self
, internal private methods don't need to follow this rule. We can and might need to create query methods to allow the object to make decisions.
It's easy to begin designing an object by specifying what it's attributes are:
class Person
attr_reader :name, :nickname, :gender
end
When we do that, we also implicitly allow other objects to use these attributes and make decisions:
if person.nickname =~ /^DJ/
person.setup_playlist_preferences('house')
else
person.setup_playlist_preferences('classical')
end
In the sample code above we're using a method setup_playlist_preferences
which accepts a single argument. The decision about what value to set is made outside of the person
object. As additional options are added to the system, this if
may have elsif
clauses added to it or it may turn into a case
statement. With public attributes, those changes can appear in multiple places in your system, which can lead to headaches when the structures of your objects change.
Alternatively, we could command the object to do what we want:
person.setup_playlist_preferences
Any decision about what to do to setup preferences can be made inside the setup_playlist_preferences
method.
Here's a summary from the C2 wiki
Very very short summary: It is okay to use accessors to get the state of an object, as long as you don't use the result to make decisions outside the object. Any decisions based entirely upon the state of one object should be made 'inside' the object itself.
One way to prevent decisions about an object from being made outside that object is to limit the public information:
class Person
private
attr_reader :name, :nickname, :gender
end
If you like this, check out the new book I'm writing: Ruby DSL Handbook which is currently half-off the final price. It's designed to be a guide to help you make decisions about how to build your own DSL and compressions of concepts.