What’s it all about?
This week was the first week of the main course. It was all about object oriented programming (“OOP“) in Ruby, more Test Driven Development (“TDD“) using RSpec and pair programming (see also this post on the basic TDD and pair programming exercise we did during week 4 of the pre-course).
This week was also an introduction to the typical structure of a week at Makers Academy. A typical week has a weekly challenge and a weekend challenge.
But first, let’s dive in and reflect on this week’s big idea: object oriented programming.
OOP and OOD
OOP is a programming paradigm that uses the concept of objects.
Ok but first of all, what’s a programming paradigm?
A programming paradigm is simply a way to classify programming languages based on their conceptual framework. Two commonly contrasted paradigms are: (1) procedural programming (“PP“) and (2) OOP.
Contrast me PP and OOP – how do they differ?
PP is a list of step by step instructions that tell the computer what to do with what data. This is how I imagined code before I started coding – I suspect it’s also how most non-coders think of code.
OOP is different. In OOP rather than a list of instructions (i.e. do this then that etc), we encapsulate (i.e. group or organise) attributes and behaviour into objects. This is neatly illustrated by the below example of an ATM system:
In the PP implementation the code is all about procedures whereas the OOP version is about interacting objects. Both achieve the same goal (an ATM system) but differ in their means.
Object Oriented Pokemon – or how to do OOP in Ruby
I’m writing this post retrospectively, so at the time of writing Pokemon Go has been released (it wasn’t released back in May) and I am obsessed.
Rather than explaining OOP through the work I did in week 1 (which I will do in brief below), I wanted to test myself by explaining OOP using Pokemon.
First things first, what’s an object?
An object is any entity that encapsulates some attributes (its characteristics) and some behaviours (what it does). The idea is to mimic the real world, which can be viewed as a massive collection of objects big and small that interact with each other so that things happen.
Using Pokemon as an example, Pikachu could be described as an object with the following attributes…
- name: Pikachu
- health: 100HP
- type: mouse type pokemon
- element: electric
- size: small
- sound: pika, pika
and the following behaviours…
- adjust HP – this adjusts the HP up or down depending on what happens to Pikachu
- cry – this produces a “pika, pika” sound which is the sound characteristic of Pikachu
- attack – this behaviour attacks another Pokemon and reduces that other Pokemon’s HP by -10 each time the attack occurs
As you can see attributes are the adjectives and nouns used to describe the characteristics of an object. Behaviours are the verbs that describe what that object does.
How do objects interact with each other?
Objects interact through messages, which change an object’s behaviours.
In the above, if Pikachu sends the attack message to rival Pokemon, Charmander, then Charmander’s adjust HP behaviour will reduce Charmander’s health attribute by -10HP.
In this way we can see that objects interact through messages that affect behaviours which can in turn modify attributes.
How do we organise objects?
But wait, there’s more. Objects can be grouped into classes. A class is simply short for “classification“. Like in the real world scientists classify animals by shared attributes and behaviors so too for OOP.
In the Pokemon example we can say that Pikachu and Charmander are both instances of the Pokemon class. The Pokemon class might look as follows in pseudo code (fake code) and have the following attributes:
and the below behaviours…
Therefore in OOP you can see we use classes to group objects that share behaviour and attributes. In this way classes are oftened referred to as blueprints, moulds or templates from which we can generate specific variations (known as “instances“) of these shared behaviours and attributes.
Why bother with classes?
Apart from the neat organisational benefits, classes save us from having to rewrite code again and again. In turn this keeps our code DRY. DRY means “don’t repeat yourself“. The more DRY the code the less repetitive and more elegant it becomes.
And that my friend, is a very basic intro to OOP. In fact… if you want to take a look, here’s the rumoured source code for Pokemon Go. If you have a dig you can see how each Pokemon and each item in the game is organised neatly in a similar way to that described above.
Ok, so back to the challenges. This week’s challenges were the following:
- WEEK CHALLENGE – Boris Bikes: a basic simulation of Boris Bike docking stations (Santander Cycles to use the official branding…), bikes and repair vans; and
- WEEKEND CHALLENGE – Airport Challenge: a basic simulation of an air traffic control system for coordinating the landing and takeoff of planes.
Each were intended to teach us how to do OOP and OOD. Boris Bikes was completed in pairs between Tuesday to Friday and the Airport Challenge was completed solo between Friday evening and 09:30am the following Monday morning.
For this challenge we had to build a basic but fully test driven simulation of Boris Bikes using Ruby tested with RSpec. Sounds simple, but it was jam packed with learning! Looking back I realise how much I learnt. I’ve tried to summarise these learning takeaways below by explaining each one in one or two sentences to give you a flavour of how much you can learn in at a week at Makers!
Single responsibility principle (“SRP”): defined by Uncle Bob (Robert C. Martin, a famous software engineer and co-author of the Agile Manifesto regarding Agile methodologies), classes, modules (see below on modules) and methods should have one, and only one, reason to change.
In other words, each of these elements in OOP should have one job. For example, a class for calculating an invoice shouldn’t also be responsible for printing itself. Doing so makes the code better organised, less fragile (i.e. a change in one area of a program is less likely to break code elsewhere), less dependent (i.e. makes code easier to maintain if our program grows) and make it easier to test because there will be less dependencies on other classes, modules or methods.
Separation of concerns (“SoC”): although very similar to the SRP, SoC is the process of breaking a computer program (as opposed to individual objects within it) into distinct features that overlap in functionality as little as possible. For example, in a website a typical SoC is the model view controller architecture which separates the program into (1) a model that handles data and logic, (2) a controller which handles routing and (3) views that render webpage pages for users.
Encapsulation: hiding the internal workings of an object from the rest of a program. By doing so it reduces the ability of other objects to directly modify an object’s state or behaviour. Just like driving a car, it’s sufficient to know how to drive rather than how the steering wheel, brakes and engine etc works.
Magic Numbers: unique values with unexplained meaning and/or multiple occurrences throughout a program. For instance something like this:
if age >= 18 puts "You can buy alcohol" else puts "You cannot buy alcohol" end
can be refactored (i.e. improved) by replacing the integer
18 with a constant like so:
min_drinking_age = 18 if age >= min_drinking_age puts "You can buy alcohol" else puts "You cannot buy alcohol" end
…thereby making clear the significance and meaning of the seemingly random integer
18 to someone unfamiliar with the code (or indeed UK drinking laws!).
Doubles, mocks and stubs: essentially creating dummy objects and methods to simulate testing scenarios… this article is already overlong so I will save this for a separate piece.
Debugging: reading and understanding error messages to fix bugs in code. One of the top takeaways was how to read and understand a stack trace (the list of error messages IRB and/or your terminal returns) and peeing everywhere…
By peeing I mean using Ruby’s print method to print out return values, objects or data to debug where and when the errors occur. This forms part of the three step debugging process: (1) tighten the loop (read the stack trace to find the line on which IRB or terminal thinks the error has occurred), (2) get visibility (pee around that line and within it to figure out what’s going wrong), (3) fix the bug!
Modules: modules are a way of grouping together methods, classes and constants. Although they sound similar to classes, they have many differences as outlined below:
Mixins: a class that contains methods for use by other classes without having to be the parent of those other classes (i.e. without using inheritance a class can utilise methods in a mixin).
…and the following further TDD/BDD concepts by using RSpec as our testing suite for Ruby:
Feature tests: tend to be written from the user’s perspective. They ensure the overall system functions as intended. By analogy, homeowners inspecting a new build home want to test the look and feel and how they might use the house but aren’t focussed on whether or not the electrics and plumbing are built to industry standards, instead it’s enough that they work as they intend to use them.
Unit tests: tend to be written from the programmer’s perspective to ensure individual methods perform as intended. Extending the house analogy, unlike potential homeowners a construction company’s building inspector will test that all the individual components and systems have been built using the appropriate materials and function individually as intended.
…and the following real life coding practises:
User stories: these simply describe one thing that the program is expected to do from the perspective of the intended user. Analysts and/or developers will work with clients to decompose their needs and wants into user stories that developers will in turn use to guide their software development process. It’s a great way to really think about the specific things the user will need from the program and why. A typical user story looks like the below:
As a user, So I can find customers, I want to search for my customers by their surnames.
Diagramming: simply sketching the attributes and methods of the various classes and any specific instances of those classes and how such objects interact through the messages sent between them.
Wow. I learnt all of that and actually how to implement these ideas into working code. Check out my Boris Bikes code here.
This was essentially a solo version of Boris Bikes, albeit to be completed solo. Unlike the week challenges, the weekend challenges do not include any walkthroughs. Instead, we were given a list of user stories (see above) and had to start from scratch by tackling each user story using TDD to drive the design of our code.
I really enjoyed this. It made me think carefully about what I wanted to achieve with the code and why. The independent nature of the challenge was awesome. Personally, I struggle with pair programming. Sometimes it can be tricky when your pair is either significantly more advanced or substantially less advanced. There’s always a bit of tension in either scenario between wanting to push ahead and taking it slow to make sure you each understand what you are actually doing.
The weekend challenge is an excellent way to consolidate your own understanding, review areas of interest or school up on things you weren’t so comfortable with during the week. It also lets you test whether you can actually do these challenges on your own and understand their constituent concepts. Without any walkthrough you’re on your own but this is great!
If you want to check out my Airport Challenge, click here.