Four Key Reasons to Learn Markdown
Back-End Leveling UpWriting documentation is fun—really, really fun. I know some engineers may disagree with me, but as a technical writer, creating quality documentation that will...
Occasionally in the life-cycle of an application we find that we need two distinct domain objects to quack alike, even if their underlying structures differ greatly. Moreover, often the second, third and thirty-ninth such objects come long after the initital object entered the application. One approach would be to shove all of the necessary functionality into the original object and add a ”type” flag of some sort. Another would be to use single table inheritance or even multiple table inheritance (here’s a gem) if we’re feeling fancy. Another approach is to decorate the objects identically so they all quack like the round-duck-in-the-square-hole you want them to be.
Consider some Widget class, persisted through ActiveRecord, with a couple of attributes and a virtual attribute:
class Widget < ActiveRecord::Base
attr_accessible :name, :feature
def display_title
"Hi my name is #{name} and I can do #{feature}"
end
end
along with, for instance, some view template for the show action (here presented in Slim):
h1 Widget!
p
' Name:
= @widget.display_title
While this may not be an optimal starting place, its simplicity makes it a great place to start. If more flexibility isn’t demanded at this point, why would you introduce decorators? At least the display logic in the view is isolated to a single method. Later, if things start to get out of control, extraction of #display_title
to a more appropriate home should be simple enough.
Say that after launch, and during the course of new feature development, a new business need arises. Our previously flimsy Widget
s have a more musically inclined MetalWidget
cousin that must come into play. These new loud, rock-themed widgets need to be displayed in exactly the same way as their humbler cousins, but with different underlying attributes. One option would be to put some business logic in the view and render a different partial depending on the Widget
type. Another option would be to leverage the Decorator pattern and build decorator classes to wrap up the two objects with a consistent interface. The first step in such a refactor is to simply extract the Widget
display logic from the view into a decorator of some type. In the following examples we will use Draper, but any implementation of the decorator or presenter pattern should work. We can move the Widget#display_title
method into a decorator, remove it from the model and update the WidgetsController
show action to decorate the Widget
s before rendering the view.
class ApplicationDecorator < Draper::Base
end
class WidgetDecorator < ApplicationDecorator
decorates :widget
def display_title
"Hi my name is #{name} and I can do #{feature}"
end
end
Then we can introduce the MetalWidget
class in order to make the application significantly louder.
class MetalWidget < ActiveRecord::Base
attr_accessor :band_name, :number_of_demo_tapes
end
In order to render these new leather-clad Widgets, we need to give them a decorator as well, so that they can be displayed in the same show action.
class MetalWidgetDecorator < ApplicationDecorator
decorates :metal_widget
def display_title
<<-BAND_STUFF
We are #{band_name}! Thanks for coming. Please pick up one of our
#{number_of_demo_tapes} excellent early albums from the site before
you go!
BAND_STUFF
end
end
So now instances of either Widget
or MetalWidget
can be displayed equally well with a single template. Another benefit of this refactoring is that the two Widget
classes actually look very similar. The virtual attribute has been pulled out so the original Widget
is smaller and more focused, and display concerns live elsewhere. Taking this idea further, it can be handy to create a common ancestor for all Widget
decorators so that a given Widget
type will never blow up in a view, even if it doesn’t implement the entire interface needed by the view. For example, we might have:
class WidgetDecorator < ApplicationDecorator
def display_title
""
end
end
class WidgetDecorator < WidgetDecorator
decorates :widget
def display_title
#...
end
end
class MetalWidgetDecorator < WidgetDecorator
decorates :metal_widget
def display_title
#...
end
end
This final step is similar to a Null Object pattern, in that it prevents views from raising exceptions if unknown methods are called. We could bring other machinery to bear here, but this feels pretty natural in the course of refactors such as the above.
With this process, and leveraging the power of inheritance, we are able to create a family of objects that all quack alike and allow us to iteratively implement the view concerns of new, but related, domain objects as they are introduced to the system. The base class of the inheritance hierarchy allows us to have reasonable default behavior, which may simply be doing nothing except not throwing an exception, and inheriting decorators can override that behavior to provide correct information to the views.
How do you build flexibility into your domain models? Do you replace conditionals with polymorphism or extract service objects, or do you have another preferred method?
Writing documentation is fun—really, really fun. I know some engineers may disagree with me, but as a technical writer, creating quality documentation that will...
Humanity has come a long way in its technological journey. We have reached the cusp of an age in which the concepts we have...
Go 1.18 has finally landed, and with it comes its own flavor of generics. In a previous post, we went over the accepted proposal and dove...