Abstract Factory Blog

Composition for Grouping Attributes

01 Jan 2013

In my previous post, I briefly mentioned that I stored instance attributes together with composed instances whose attributes I could fetch via instance.composited_instance.attr

Is composition good for this sort of thing?

In books, you might find composition to be used for grouping functionality by letting smaller objects form larger objects via nested encapsulation. E.g. instead of storing name, home_number and work_number under Person, you could instead choose to store home_number and work_number under a Contact object that is then stored under Person.

o Person
    o .name
    o .contact
        o .home_number
        o .work_number** **

So whats the benefit of doing this? Well, besides the benefits of composition in general, you get to stow away accessors that might not be directly related to the overall operation of the object into less obvious paths, thus encouraging the use of parts of the functionality over others. The directly available accessors is intuitively more comfortable to use than to go 6 level deep for a simple operation.

>>> you.name() # This
"marcus"
>>> you.personaldetails.contactdetails.publicinformation.name() # vs. this
"marcus"
>>> # Which do you prefer?

The neat thing about the latter is once you start gathering functionality that relates to the bigger picture of the parent object, but aren’t really necessary for everyday use. Remember, the idea is to make the interface as simple, yet as complete, as possible.

>>> you.countries_visited()
['france', 'russia', 'usa', 'finland', 'sweden']
>>> you.history.travel.countries_visited()
['france', 'russia', 'usa', 'finland', 'sweden']

Storing that level of detailed functionality directly into the parent object can easily obfuscate it’s interface. That’s why it can be beneficial to stow them away somewhere where, when accessed, the user knows he’s dealing with less common functionality.

An alternative to composing accessors is to collect external sets of functionality. Rather than storing all functionality inside an object and it’s composed objects, you can choose to store it in separated functions, either in the same file or in different modules or packages.

>>> you.nearby_cinemas()
['canary wharf cineon 1.5km', 'north greenwich odeon 0.4km']
>>> externalmodule.position.nearby_cinemas(you)
['canary wharf cineon 1.5km', 'north greenwich odeon 0.4km']

As you can see,** you** is no longer responsible for processing this command. Rather, an exernal module takes care of the necessary steps in order to produce the result. This can lead to deep and thorough functionality whilst still providing a simple interface for you. The user now has to think about what tool is right for the job as opposed to figuring out how to use one tool for everything.