Booleans. These program control switches, they introduce branching in code and do not allow to have linear path for code execution. That is fine, because in the real world, everything is the result of another action prior to itself.
A boolean method is a method that returns a boolean result, either true or false, or truthy or falsey values. You rarely see methods being created just for the sake of in/equality checks. It is preferred to perform the check inside the code as it's more obvious what attribute are we checking the equality for.
Writing equality checks inside boolean statements does not give any context as to why this attribute is checked and not another attribute. In other words, the domain knowledge is lost.
The domain is the problem which the code tries to solve.
Imagine a set of objects that looks like this,
class Person attr_accessor :name def initialize(name) @name = name end end class Car attr_accessor :person def initialize(person) @person = person end end
If we want to determine if a car is owned by a business. We usually perform a check against the person attribute of the Car object.
john = Person.new "John Doe" alyn = Person.new "Alyn Oakenfist" ford = Car.new person
Given these three objects above. To determine if ford belongs to john or alyn, we can write a conditional that looks like this.
if ford.person.name == john.name puts "owned by John" else puts "owned by Alyn" end
Notice the condition we're checking. There is no context given around the code as to why we're checking the person attribute. Some domain logic is lost here, we know why we're checking. But what about someone who did not write the code, or even, our future self.
I've grown very fond of the idea of representing these conditions as domain concepts--methods of an object. I like to ask myself why the check is performed? The answer is: the check is performed to determine if car is owned by a person. In the answer, i can extract the domain concept, i.e ownership of a car, and give it a (method) name.
Let's do that, let's add a method inside the Car object that determines if this Car is owned by a person or not.
class Car def owned_by?(person) self.person.name == person.name end end
The condition above then, can be simplified into
if ford.owned_by? john puts "owned by John" else puts "owned by Alyn" end
Not only the resulting code is simpler. It also saves the caller the pain of having to memorize attribute names
.person.name, they can simply call the
owned_by? method without having any knowledge of it's underlaying implementation. Another bonus is that this code is more maintainable. Because the reference to
person is only made inside the Car object, in the future, if this attribute is changed, or the attribute we check the equality for changes, it's a simple one-line change instead of finding and replacing everywhere we're accessing
Some domain concepts are bidirectional. Both objects can perform the equality check. In this example, a Car can be owned by a Person. Also, a Person can own a Car. In these cases, i like to add the method to both objects. At first glance it might seem like duplication. But as with everyday life, the context in which the code is written differs. So, having different methods exposed on different objects enriches the vocabulary used inside the code.
class Person def owns?(car) name == car.person.name end end
Instance methods that return a boolean results are an important part of the program. They allow to capture domain concepts and gives the a name inside the code as opposed to letting them float inside the codebase.
Hope you enjoying this. Have a wonderful time coding.
Top comments (2)
I would do:
And override eql? or == never sure which
That what i also would do! But i wanted the code snippets to be as simple as possible so that newbies(who haven't seen the spaceship operator, or people who don't use Ruby) could follow along easily.
Thanks for the note!