Next / Previous / Contents / Shipman's homepage

13.2. Sighting.getLocGroup(): Find a sighting's effective locality

The purpose of this method is to find out where a sighting occurred. Due to inheritance, however, the effective location may live in the Sighting instance, or in its parent BirdForm, or it may be the default locality for the day, which resides in the related DaySummary.

While working on an earlier version of this module, the author became embroiled in an interesting discussion involving the Law of Demeter. Consider the case where a sighting does not define a location code, so that it inherits its location code from the DaySummary. How are we to find out the day's default location?

In the original version (with different names), this ugly code I wrote drew fire from colleagues:

        locCode = None
        if  self.locGroup is not None:
            locCode = self.locGroup.locCode
        if  locCode is None:
            if self.birdForm.locGroup is not None:
                locCode = self.birdForm.locGroup.locCode
        if  locCode is None:
            locCode = self.birdForm.dayNotes.daySummary.defaultLoc

That is, if the sighting defines a location code, use that; if not, and the parent form defines a location code, use that; otherwise, go up to the form element's parent day-notes, then down to that element's day-summary, and dig out the default location code.

However, this violates the Law of Demeter, which in the succinct form quoted in the Wikipedia article above, says “Only talk to your immediate friends.” Dr. Allan Stavely suggested a much cleaner approach:

  1. If the Sighting instance defines a location, use that.

    If the Sighting has no location code, ask the parent BirdForm instance what location code it uses. In this code, that uses the BirdForm.getLocGroup() method.

  2. The BirdForm.getLocGroup() method uses its own .locGroup value if there is one; if not, it interrogates the parent DayNotes.defaultLoc() method for the default location.

  3. The DayNotes.defaultLoc() function is a pass-through that interrogates its daySummary.defaultLoc() method.

The payoff here is that the Sighting element does not need to know anything about the internals of two classes that are not its immediate neighbors: DayNotes and DaySummary.

birdnotes.py
# - - -   S i g h t i n g . g e t L o c G r o u p

    def getLocGroup(self):
        """Find self's effective location.
        """

First we ask the parent BirdForm instance for a LocGroup that describes the locality from which we may inherit. See Section 12.6, “BirdForm.getLocGroup(): Find the effective locality”.

birdnotes.py
        #-- 1 --
        # [ parentGroup  :=  a LocGroup representing the
        #       effective locality of self.birdForm ]
        parentGroup = self.birdForm.getLocGroup()

If self.locGroup is None, we'll just return the parent locality. Otherwise we use inheritance to form the sighting's effective locality; see Section 14.3, “LocGroup.inherit(): Implement inheritance for locations”.

birdnotes.py
        #-- 2 --
        # [ if self.locGroup is None ->
        #     return parentGroup
        #   else ->
        #     return a new LocGroup made from self.locGroup,
        #     inheriting from parentGroup ]
        if  self.locGroup is None:
            return parentGroup
        else:
            return self.locGroup.inherit(parentGroup)