Type-safe data structures and bananas without gorillas in PHP
I used to love everything about object oriented programming, ignoring everything else, when I finally got it, I thought that every piece of software should be developed using OO and if it is not, it is wrong.
Design patterns, SOLID, GRASP etc, are principles in OO to solve problems that OO itself causes, not to essentially make it better.
But first, a PHP problem. It lacks type-safe data structures. You can’t strictly set types to properties, one way to solve it is to define getters and setters. I’m going to use a reduced version of a getter/setter in the examples to ensure type safety.
Let’s imagine that we have a
Swimmer with a
name and a
wetsuit are strictly Strings. Can’t be any other thing. If they are public properties we could set Integers to them. And our
Ok, let’s dive into an OO problem. Imagine that now we have a Cyclist, but who can also
swim(), like we see in triathlon competitions.
Well, imagine that now we want a simple
Cyclist, who doesn’t
swim(). How to accomplish it? Extract
swim() to a
SwimmerCyclist? What if we need a
Runner for a complete Triathlete?
Triathlete who implements Interfaces and use Traits? Then classes will be only type declarations and implementations will be divided into Traits? How to test all this? OMG!
The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle. — Joe Armstrong
If you have referentially transparent code, if you have pure functions all the data comes in its input arguments and everything goes out and leave no state behind it’s incredibly reusable. — Joe Armstrong
What if we split behavior from data into pure functions?
run() as pure functions expecting exactly what they need to be called successfully?
First, let’s define our type-safe data structures using interfaces:
And the implementations that we’ll be suffixing with
But let’s keeping going and add our pure functions for each behavior:
And at least, the proof of concept, the Triathlete:
Now we can have all kinds of behavior reusability, everyone can
swim() if it’s a
Swimmer, everyone can
ride() if it’s a
Cyclist and everyone can
run() if it’s a
Triathlete is all three and it can use the exact same behavior of them.
What you think? Have you already some thoughts on this approach? There are some drawbacks of splitting data and behavior? Leave some comments below.
P.S.: I know, it is a lot of boilerplate code to ensure type safety.