This week was a continuation and consolidation of the OOP, ODD, Ruby and TDD topics covered in week 1.
The weekly challenge was Oystercards: a TDD Ruby sim of London’s Oystercard system. The weekend challenge was to create a very basic takeaway ordering system that sent a confirmation text message using Twilio. Again, this was done with TDD Ruby.
This project went fairly smoothly when simulating the basics of an Oystercard, e.g. maintaining an account balance and topping it up and deducting fares from said balance. My pair and I breezed through all of this by 4:30pm Monday. As it was a Bank Holiday Makers Academy was closed so I met my pair at Canary Wharf to code in Eat.
But then as the week wore on it got massively more complicated and abstract when we started trying to understand and code up how journeys (tap ins and tap outs) and journey logs (i.e. records of each journey) would work together with edge cases such as tapping in but not out, vice versa and not tapping in at all etc.
Given the increasing complexity, we needed to begin extracting behaviour and attributes from our existing classes into further classes. This process is known as class extraction. It drove me insane.
Class extraction… like pulling teeth without SRP
Class extraction is a simple concept tied to SRP. If you follow the SRP every method and class should have one and only one responsibility. If you don’t follow SRP from the get go it becomes very tricky to decouple code and extract behaviours and attributes to separate classes without breaking the surrounding code and each of your tests.
Here is a basic example of class extraction:
As you can see, the original Person class contains several attributes: name, officeAreaCode and officeNumber. It also contains a method, getTelephoneNumber. We can see that the original Person class contains information that does not directly relate to a Person object and can be split into a second class for Telephone Number objects.
By doing so, the purpose of each class becomes clearer. Person class is now responsible for creating Person objects that can use the getTelephone method to retrieve that person’s telephone number by sending a message to objects of the Telephone Number class, which will contain that person’s area code and office number.
As you can imagine, if you try to keep things separate from day one, e.g. simple methods of one to two lines of code with a single responsibility, it is not only easy to identify what might belong to another class but also easy to extract those methods to that class without breaking the surrounding code or your tests.
The main takeaway (at least for me) from the Oystercard challenge was: adhere to single responsibility principle at all times + deviate from it at your peril.
No this isn’t some sort of substance addiction issue for coders. It’s a coding technique. It’s often massively overcomplicated but is in reality quite simple. This topic was introduced to us this week and is often used in conjunction with the SRP.
Why are dependencies bad?
Before beginning, it’s worth explaining why dependencies are bad.
Let’s use a real world example: my friend Tom. Tom works in security and has to travel a lot with his work to pitch and sell his company’s security software product to a variety of organisations. As such Tom has to organise his own travel. Compare the following:
Scenario 1 – Lots of Dependencies
Tom has to remember all the various booking agency contact telephone numbers, email addresses, websites and company discount codes and membership details etc to make his own bookings. Any change to those agencies would require him to re-learn a new set of information each time.
Being a smart company, his employer often shops around for the best deals so Tom has to re-learn these details quite frequently.
Tom’s a smart guy so that wouldn’t be too much trouble. However, it’s distracting from Tom’s real job, selling security. In fact, all Tom needs to know is where and when he needs to travel and for someone else to book it for him.
Understand also, Tom is one of many sales persons at his company. If the company changes booking agencies, whole teams of people now need to re-learn new booking details and processes before they can travel anywhere. Whilst this is happening they aren’t out selling and the company loses sales.
In this scenario, these dependencies are closely coupled with Tom and his colleagues: each must know the specifics of how to book their own travel when this is totally unnecessary and potentially disruptive.
Scenario 2 – Dependency Injection
Instead, it makes sense to inject dependencies. To do so, Tom’s company could (as I am sure it already does) introduce an internal bookings team who deal with travel.
Now Tom simply needs to call that person up and tell them where and when he needs to travel. That person has one responsibility: to know the company’s booking agency details and make bookings.
This allows Tom to get on with his job (selling software). As a result, the interface between Tom and the bookings person will remain unchanged whichever bookings agencies the company chooses.
From Tom’s perspective, he now calls up the internal bookings person, tells them where and when he needs to travel and receives a confirmation back. Tom does not need to know and is unaffected by any changes to the company’s booking agencies. As such, his job is no longer disrupted each time his company switches booking agencies.
Turning to code, here’s a very simple code example:
class Pokemon def greet(name) Greeter.new.say_hi(name) end end
The above is smelly code (code that is bad). It has several dependencies. Having many dependencies is bad because it closely couples code.
According to POODR (see below), an object depends on another when:
- It knows the name of another class
- It knows the name of a method of another class
- It knows the arguments that a method of another class requires
- It knows the order of the arguments necessary for a method of another class
Our Pokemon class has all four of the above dependencies. Taking each in turn:
- Pokemon class knows the name of the
- Pokemon class knows the name of the
say_himethod of the
- Pokemon class knows the arguments of the
say_himethod of the
- Pokemon class knows the order of the arguments of the
say_himethod of the
Naughty Pokemon! Let’s level up our Pokemon class using dependency injection or “DI” as it’s often abbreviated.
class Pokemon attr_reader :greeter def initialize(greeter) @greeter = greeter end def greet(name) greeter.say_hi(name) end end
Now when we create a new instance of the Pokemon class by doing
pickachu = Pokemon.new(Greeter.new) we pass in a new Greeter object containing the Greeter object’s greeter methods.
This way we reduce the dependencies from four to two. Now the Pokemon class only knows that (1) the
greeter object passed in on initialisation has a method called
say_hi and (2) that such method takes one argument (a name).
It’s that simple, however, it can get quite abstract as the examples become more complicated. And that brings me to an excellent resource on this OOD topic and many others…
After the class extraction struggles described above, I decided it was time to school up on ODD. To do so I started reading Sandi Metz’s Practical Object Oriented Design in Ruby or “POODR” for short. It’s an excellent book.
Sandi explains very clearly the principles of OOD through lots of examples, not only explaining the how but the why so it’s clear where and when OOD can make all the difference to your code.
I also love the book because it’s acronym, POODR, reminds me of how my younger brother, Robert, would refer to our first computer that my dad brought home from his office when we were little boys!
Having read a lot of POODR on the bus I started to understand class extraction and dependency injection a lot better.
The (almost) finished product…
I never quite finished the Oystercard challenge, but check out the almost finished end product here.
The weekend challenge was to create a test driven takeaway ordering program that uses the Twilio API to send order confirmation text messages. This was essentially very similar to the Oystercard challenge in content and concepts apart from the integration with the Twilio API.
The challenge was quite tricky but I managed it better than the Oystercard challenge, mainly because I was laser focussed on single responsibility throughout, in particular the refactor stage of my TDD. This made it far easier to identify attributes and methods that would be better extracted to their own classes.
Using the Twilio API was the last step. It was quite straightforward to integrate. The first step was to install the Twilio Ruby gem. After that it was simply a case of setting up the configuration files and signing up for a trial dev account at the Twilio website.
It was my first introduction to APIs. Receiving a text message from my app confirming a takeaway order felt like a great achievement as it seemed like I’d finally made a very ‘real world’ app, albeit one run from the command line.
I realise I’ve introduced a new term here: API.
API means Application Programming Interface. It’s simply a set of functions and procedures that allow the creation of applications which access the features or data of an operating system, application, or other service. Ok, so why bother?
Simple: don’t reinvent the wheel. If someone has already invested time and effort into solving a complex (or even a simple) problem and is willing to share that knowledge then you should borrow as much as you can.
For example, apps like Uber that rely heavily on mapping will connect with the Google Maps API to use Google’s mapping data and routing algorithms to present a new experience, i.e. on demand private hire cabs with a map showing where your cab is.
Back to Takeaways…
So that’s APIs. Twilio is simply an API for messaging, voice and video and authentication systems. By creating an account with them and completing a few configuration steps you can build upon these tools and add them to your app without going to loads of extra effort, ensuring you remain focussed on your product rather than reinventing the wheel.
This was a great exercise because many apps rely upon APIs so it was good to understand what they are, how they can be used and why they are used.
The main downside to this challenge was that it made me crave takeaway food rather too much! Check out my Takeaway Challenge code here.
A tough week
This week was both good and bad. I really enjoyed consolidating my OOP and OOD knowledge, in particular reading around both topics via POODR.
The class extraction during the week was rather demoralising as I spent 3 days trying to complete those steps of the Oystercard challenge.
This due to the fact my pair and I made fantastic progress on the Monday, completing all mini challenges up to that point. As such, I ended up ahead of my pairs Tuesday to Friday, meaning I had to keep going back to near the beginning of the challenge and repeating those steps with my pairs.
Whilst I got some consolidation benefit through explaining these steps to my pairs, it did become a little tiresome and frustrating as the week end approached and I was no further advanced in the challenge than I was on Monday.
That’s pairing though: sometimes it works very well and you have a succession of pairings that work really well where you both move along at a similar pace or several where you are constantly going back and repeating things. Although that repetition is great for understanding, after one or two repetitions the utility of doing so starts to wane.