Goodbye Getters and Setters — Part II
So, in my pervious article, we saw how we can preserve encapsulation — one of the most fundamental principles of Object Oriented Design (OOD) — by “putting the behaviour where the data is”.
But now, when we come to actually make use of these beautifully encapsulated objects, we come across what seems like a problem and it appears that we will, after all, require access to those properties that we have so neatly encapsulated. Why? Well, we want to be able to present our proverbial customer/user with details of how much money they have on their Card. And how they heck are we going to do that if we have no access to the internals of the Card object??
Our first program attempting to make use of our model looks like this:
Great. But now, after topping up the card with some money from their Account, our dear customer wants to know how much money they have on their Card. We need to print out the balance available on the Card to the screen and our first thought naturally is to write something imperative which looks like this:
echo 'You have £' . $card->getBalance()->getAmount() . ' left on your card';
But almost immediately you are hit with the horrible realisation that your Card has no getters (and what’s worse — nor does the Money class). Damn, we’re going to have to add that stupid getter back on our Card and break its encapsulation, otherwise how are we going to access its balance??
No. Hold on a moment. Again, we’re stuck in our imperative mode of thinking. Lets try again to apply that “put the behaviour where the data” adage again. We’ll give the Card a printBalance() method. Yay! Now we don’t need getBalance() because we’re doing the printing inside the Card class:
We still have to deal with the, relatively minor, issue of requiring a getter on the Money class. We can solve this quite easily by giving Money a format() method, which takes a format string as its parameter and returns the amount formatted the way we require:
public function format(string $format): string
return sprintf($format, $this->amount);
And now we can rewrite our printBalance method in the Card class like this:
public function printBalance()
$message = sprintf('You have £%s on your card', $this->balance->format('%.2f'));
echo $message . "\n";
This is still problematic though, for two reasons:
1. The how of printing the balance is in the Card class —even though it doesn’t really belong there
2. We’ve “hardcoded” the output device to stdout
Ideally we want to be able to decouple the work of how to print the balance from what to print as the message and in order to do that we’d either need to inject another service class as a dependency (e.g. construct Card with a Stdout class) or instantiate a Stdout class in our main.php file and do something like
$stdout = new Stdout();
The former solution, of injecting Stdout into the constructor of Card (alongside the initial value object of Money), is problematic, not least because, in DDD parlance, our Card is an “entity” and Stdout is a “service”. And, if you know something about DDD, you might be aware that every time you inject a service into an entity, God kills a kitten. Now, you may not care too much about kittens or DDD, but for reasons I won’t go into here — this is something you shouldn’t do.
The latter solution doesn’t break any DDD rules but would still require the Stdout class to access the Card balance and therefore break its encapsulation.
What to do?
Double Dispatch to the Rescue!
There’s a little used technique in OO programming called “double dispatch” (which I know sounds like a type of rope skipping game). The “dispatch” here refers to simply calling a method on a class and the “double” implies calling another method where normally one call would suffice. The reason for the second call is to facilitate decoupling and the preservation of the objects encapsulation.
To remind you — our objective is to be able to be able to present the balance on the Card without having to break encapsulation or kill kittens. And the way it works is like this:
We instantiate our Stdout object (which implements an OutputDevice interface) like so:
$outputDevice = new StdOut();
Then we pass this object to our Card objects printBalance() method and use this method from within the Card object — and now our main.php and Card class look like this (bear with me, it will become clear why we’re doing this in a moment):
Where the “double dispatch” comes in is the call to the method print() on the OutputDevice, which is called within the Card class (which is where we have access to the cards balance and where we create the message the customer wants to see). Whereas the implementation of Stdout contains only the logic required to output a string to stdout (and of course we can now also easily replace Stdout with Printer or MobileDevice or any other device the customer would like the message to be printed to):
So now, not only have we successfully keep the encapsulation of our objects intact, we’ve also managed to end up with the responsibilities in the right place:
The Card sends a creates a message with its balance, which it sends to and OutputDevice, and our main.php sends a message to our Card telling it to print its balance to our chosen output device.