← Back to Upcase

Live Coding Session: Replace Conditional With Null Object


(Upcase ) #1
Ben and Joe do a little live coding, demonstrating how to extract a Null Object from an existing class in order to encapsulate conditional logic related to nothingness. If you'd like to play along, we have two exercises for practicing Null objects...
This is a companion discussion topic for the original entry at https://thoughtbot.com/upcase/videos/live-coding-session-replace-conditional-with-null-object

(Jack Franklin) #2

Really enjoyed this episode and found it useful, thank you.

I’ve just got one question relating to when you added the last_or_none method as a private method. Why do you define it as self.last_or_none, and what’s the difference between the two in this context? To me it looks like you define a class method, but then you call it on the instance that ActiveRecord returns? If someone wouldn’t mind clarifying that for me that would be great!

Thanks,

Jack


(Rafael George) #3

@jferris Great stuff as always; I was thinking in particular in the fact that the title of the screencasts says ‘remove a conditional with a null object’ which in the end is not exactly what you are doing here, you move the conditional inside a model are there any advantages of moving into that path as far as I can see I see a new abstraction which I now will have to maintain. In another note I have mix feelings to be honest just because now I can see a name that’s says there is no answer as oppose to a nil traveling around in the stacktrace maybe that’s exactly the intention of your refactoring; but I rather prefer not to conclude on something and wait for your answer :wink:

Thanks


(Joe Ferris) #4

You’re right - it’s being added as class method. That’s what the self. does in that example.

Class methods are how you generally interact with ActiveRecord’s querying layer. Each ActiveRecord subclass is an API to interact with one SQL table. The most direct way to use that API is to define class methods on ActiveRecord subclasses.

This gets confusing, ActiveRecord’s relation objects - the objects returned by scopes, has_many associations, and chainable methods like where - look for class methods. For example, if you call user.posts.published, this is actually looking for a published class method on the Post class.


(Joe Ferris) #5

That’s true. In that particular example, we’re just moving a conditional.

However, it also encapsulates the condition, which is useful. This can remove duplication by handling that condition just once. It also can make code easier to understand, because it brings the decision close to the related data.

Using the Null Object pattern is about reducing the number of nil references being passed around. Because nil is a failure case and doesn’t really follow duck typing or polymorphism, eliminating nil references will generally eliminate bugs.

Although it does introduce indirection, it also has the effect of reducing the amount of overall noise in code. Passing nil around results in a lot of extra if statements, try calls, and boolean operators like ||.


(Nicholas Mertaugh) #6

I have a general NullObject question. I’ve been replacing some conditional logic with a NullObject and am stumped trying to refactor a boolean expression that looks something like this:

@some_var = some_method_that_returns_nil || SomeClass.new

I want it to read more like this:

@some_var = some_method_that_returns_a_null_object || SomeClass.new

But of course that doesn’t work, because:

NullObject || SomeClass.new
# => NullObject

Is there any way to override the operator here? Or am I in smell land?


(Franzé Jr.) #7

I have the same question.


(Ben Orenstein) #8

You’re in smell land :smile:

With null objects, you want to be able to treat them just like real objects. You don’t want to have a way to ask them “hey, are you a real object or are you null?”, because that just adds back in the conditional logic that you’re trying to remove.

You had this before:

@some_var = some_method_that_returns_nil || SomeClass.new

I think what you want is this:

@some_var = some_method

def some_method
  if something
    SomeClass.new
  else
    NullObject.new
  end
end

(Nikola Novakovic) #9

Do you recommend to always write tests for NullObject type of classes ? :slight_smile:


(Rikki Pitt) #10

Thanks for this chaps, I’ve implemented null objects in my current project with a NullSubscription class to make things a little neater when building an account page.

Really cleans things up, cheers for the tip!

Rikki


(Igor Galvão de Britto) #11

What about implementing a NullObject that inherits from the base object? Then you would have all methods available, possibly hooking appropriate responses to the ones that prove needed.