or, It’s Mine! All Mine!
Ownership is an important concept. What does that mean when talking about entities or objects? It’s all about control, creation, and monitoring. I’m going to distinguish two cases of ownership: by instance and by type.
Lets start with a bowling ball and a bowler. I’ll be using Groovy for examples in this post, because I can use strong typing only where I need to and not have to write as much boilerplate as I would in Java.
The problem with this code is that the Bowler doesn’t really own his BowlingBall, does he? He can be given any old BowlingBall instance at any time through setBowlingBall(), and even when the Bowler has it, someone else can change his pet name for it through getBowlingBall().setPetName(). Not nice.
The traditional solution is to declare BowlingBall a private inner class, and instantiate an object of that type during the construction of Bowler. That way, a Bowler can’t be given any old BowlingBall, just the one that it created for itself.
Also, in order to change a property of BowlingBall, you should have to go through the owner. The owner then delegates the property change to a method on BowlingBall (which can’t be accessed outside Bowler). The following Groovy code illustrates a simple ownership model. While I’m at it, I’ll define an IBowlingBall interface, which will prove useful later.
So that takes care of a Bowler, owning a single BowlingBall. What if a BowlingBall can be owned by more than one type of entity, though? Not shared among instances, mind you, but shared among Class types?
A more complicated case
To illustrate, let’s formulate use cases and constraints regarding bowling balls. Here they are:
- A bowling ball has a brand and serial number
- A Bowler’s bowling ball can have a pet name
- A bowling ball can be owned by a Pro Shop or a Bowler, but not both
- A bowling ball’s properties can be mutated, but only by the Pro Shop or Bowler
- A bowling ball cannot exist in and of itself, it must belong to Pro Shop or Bowler
Ownership by Instance
Both entities, ProShop and Bowler, need instances of BowlingBall, but if BowlingBall is a static private inner class of Bowler, that can’t happen, and I would rather not have to maintain two separate but identical class definitions in both owning Classes.
I could make both ProShop and Bowler extensions of an abstract class called BowlingBallHolder, which contains the actual BowlingBall definition. The wrinkle here is that a pro shop doesn’t own just a single bowling ball (neither do serious bowlers, but for this article we’ll say “one bowler, one ball”). Therefore, a common abstract base class for ProShop and Bowler isn’t going to work well.
If we put the BowlingBall definition in a Factory class, however, that factory can construct private inner class BowlingBall instances and ensure that before they leave the Factory, they are owned by either a ProShop or Bowler. Let’s take a look at what that Factory might look like:
There are a couple of problems with this. One is that pro shops typically don’t assign pet names to their bowling balls in stock, so those get/set methods don’t really apply for ProShop’s BowlingBalls. The other is that I’ve had to reimplement Bowler.setBowlingBall() in order for BowlingBallFactory to give a bowler a newly created BowlingBall. Though I trust BowlingBallFactory to do the right thing, the set method could be call by any old class (not just the factory) at any time — not a good thing.
First thing to do is to reclassify our IBowlingBall interface into IBowlerBowlingBall and IProShopBowlingBall, with methods appropriate for bowling balls in those contexts. The next thing to do is get rid of setBowlingBall() (and remove ProShop.addBowlingBall() as well) so that devious unreliable objects other than BowlingBallFactory can’t swap out our Bowlers’ balls with impunity (did I just type that?).
The solution to the second problem is to use a simplified Visitor pattern, allowing a visiting BowlingBallFactory to indirectly use a private BowlingBall.setBowlingBall() method.
Ownership by type
So I want to point out here that we’ve subtly transitioned from talking about objects owning other objects to object types owning other types. Let’s take a look at the new code listing. There’s a lot of additional goings-on here, which I will explain in the comments following.
Defines a base interface and an abstract base implementation. IBowlerBowlingBall and IProShopBowlerBowlingBall extend the base interface IBowlingBall. By defining these interfaces, we can ensure that all instance of Bowler and ProShop contain the correct types of BowlingBalls. This is what I mean by ownership by type. I cannot hand a ProShopBowlingBall to a Bowler. Note that I ProShopBowlingBall is a marker interface, used only to distinguish BowlingBalls by type.
Next, two new list classes are declared, one for IBowlerBowlingBalls and the other for IProShopBowlingBalls. Here I am using Groovy’s Delegate annotation to automatically extend each class with delegate methods that all Lists implement for the bowlingBall fields. In Java, I believe you would have to do this “manually”. IBowlerBowlingBallList uses Array.asList() to fix the List size to 1.
The BowlingBallFactory. Two inner classes hide away the implementation of IProShopBowlingBall and IBowlerBowlingBall. In order to avoid having to create package- or public- bowling ball mutator methods to IProShop and IBowler instances (addBowlingBall(), setBowlingBall()), I add to visit() methods. Now we see the reason for the new List classes declared in lines 27–33: method polymorphism. The methods are distinguished by parameter type, something that couldn’t be done with generic List parameters because of type erasure (which would make both visit() methods identical). The createBowlingBallFor() methods are also polymorphic.
The Bowler class is similar to what was shown in the previous listing, however the BowlingBall instance is stored in a list, which acts as a reference holder that can be passed to the BowlingBallFactory for assignment.
ProShop is pretty straightforward. Not much to explain here.
A not very thorough happy path test to show that all is working as expected.
The code enforces tight restrictions on ownership, both by instances and by types. Instance ownership is maintained by the Factory and use of the Visitor pattern; type ownership is maintained through interface definitions and polymorphic methods.
The advantage of defining strict ownership requirements is to limit access to properties, and to add points of control where listener’s can monitor changes to the state of owners.