Is your code singly-responsible?

Siobhan Baker
Apr 17 · 7 min read

In this post I’ll be discussing what I’ve learned about Single Responsibility, Encapsulation and Polymorphism whilst coding a Tic Tac Toe game as a new Apprentice.

Over the past couple of weeks these are terms which I’ve heard mentioned but couldn’t describe yet. I had no language for them or what they meant for my code. However as I’ve worked on building my application and made inevitable mistakes, the importance of these concepts has started to hold together a little more. More notably, how their interaction with each other informs the overall architecture that I should be aiming for in my Tic Tac Toe application.

What follows is my understanding of:

  • the Single Responsibility Principle
  • Encapsulation
  • Polymorphism, and
  • Duck types

These are each broken down into three sections. As my application is written in Ruby, I will speak about the internal workings of my classes by referring to methods. I also use the example of a class throughout most of my explanations of these terms.

Image of a single chess piece by Sebastian Voortman | Accessed at: Pexels.com

Single Responsibility Principle (SRP)

What is it?

This is the idea that within your code, each piece of functionality should have a single focus. Using the example of a class, a class should be clear on its purpose and should only deal with the things specifically aligned to that purpose, e.g. being and acting on a Board. Concerns about the detail of Board’s actions should be neatly severed from the rest of your codebase (the collection of files related to your application), so that if the way in which these actions are taken changes, they only need to be updated in one place.

James Ellis-Jones describes this well here:

Any change required of a code system will naturally need changes to the body of the code at a number of different points. If the system is structured following the principle of ‘gather together those things that change for the same reasons’, you will minimise the number of modules (classes) that need to change. This allows you to hide the change as much as possible behind encapsulation boundaries, thus stopping the change cascading out into the rest of the system, and reducing what needs retesting because it might be broken.

Why does it matter?

SRP matters for various reasons, some of which is further explained by my next section on encapsulation. Its main advantage is that your code is cleanly separated out into logical divides, with each section solely concerned with doing its tasks exceptionally well and knowing exactly how to do them. For a class, this makes it easy to make changes to the internals — data structures, methods, variable names — of your class as needs be, and update your tests without having to go anywhere else in your codebase.

How to achieve it?

As you build your application try to think about what parts it needs in order to achieve its overall aim. Separate these individual parts out into nouns describing what they are, which will become your class names. Then, for each method within your class separate them into verbs, which are purposefully aligned actions that your class can perform (also described as behaviours your class has). Again think very carefully about the naming of these methods and what they do. Methods should be singly focused and ideally each able to achieve this focus within a few lines of code.

For reference, there’s a nicely described explanation of SRP here.

Image of a lock by Chris Barbalis | Accessed at: unsplash.com

Encapsulation

What is it?

Following on from SRP, once you have classes which are singly responsible, each class should also be very guarded. Whilst it can play alongside other classes within the application and even exchange messages with them, it’s very private about how it does what it does.

A class will usually have some methods which are private, which help it to do what it needs to from within. It may also have some public methods which other classes know about and can send messages to, and expect a response to be returned. However, they still shouldn’t know anything about how that response is generated, simply that they receive a response which they then know what to do with.

My visual description of encapsulation using a lock and key.

In the above drawing Lock takes care of the steps needed when it’sunlock method is called by Key. If your lock then gets upgraded to more complex security lock needing extra steps unlocking, only your lock class knows the details of the unlock method, and Key can continue to call unlock with no disruption.

Why does it matter?

Thinking about SRP, by ensuring your class is singly responsible and then safely encapsulated, you can be confident that your class has control over its internal activity, making it much easier to make minor (or major) changes when needed.

If you decide to change the detail of that class’ methods as your application grows either in size or complexity, you should be able to do this safely without the worry that multiple tests outside of that class will break. Classes shouldn’t care about how another class does what it does, just feel safe in the knowledge that they can ask it to do something and it will.

How to achieve it?

Think about how reliant your class is on knowing intricate details about another class in order to achieve its functionality. This may raise questions about whether your classes are safely encapsulated and whether they are indeed singly responsible. If you have the time, test your application by making a change to a data structure, (e.g changing how my board is stored from an array to a hash) and see what happens when you do.

Polymorphism

What is it?

Polymorphism put simply, refers to one action which is performed in different ways. As I’ve come to understand it, polymorphism is generally supported by having parent and child classes. As children of a parent superclass, subclasses inherit behaviours, but how they enact those behaviours differs.

Why does it matter?

Polymorphic classes support encapsulation as they allow multiple child classes to inherit the same properties and behaviour. An external class which needs to interact with these multiple children doesn’t need to know which child it’s interacting with. It simply knows that all of them have a method that it can send the same message to and receive a response from. How that specific child produces that response is of no concern.

If you were building an application for a store which needs to deal with multiple product types but which all have the same properties, e.g. weight, size, etc you can have one other class which can call .weight and .size on all these different products easily and receive those values.

My visual description of what my class structure for different player types in Tic Tac Toe might look like if it were polymorphic.

How to achieve it?

Thinking of the picture of a chessboard below, in a game of chess you might have a parent class ChessPiece. Child classes of ChessPiece would be the various individual pieces such as Pawn orRook which all have a method move. According to the rules for how that piece calculates and makes it’s move, the details of how they produce their move will differ. However our Game will simply be concerned that it can call piece.move, and will receive a response from whichever child move is called on.

Image of a chessboard by Randy Fath | Accessed at: unsplash.com

Duck types

What are they?

Whilst I’d originally thought that this was exactly the same as polymorphism, it turns out that there are subtle differences between the two.

As I don’t actually have a Player superclass in my application, my Human and Computer are simple duck types which my Game is able to interact with when it calls current_player.get_move. In this way, my Game only cares that the class it’s interacting with has a method name which matches the one it’s looking for and that it gets a response that it can handle.

My visual description of how my Human and Computer classes are duck typed

Why do they matter?

This maintains SRP and encapsulation as Game doesn’t have to be concerned with whether it’s interacting with a ‘duck’ or not, just that the thing quacks when asked.

How to achieve them?

If you create a method of the same name in different classes, which can be called by a separate class, you have created a duck type. Be careful to ensure that:

  • this is intentional (in all other circumstances you wouldn’t usually have the same method name across classes in your application), and
  • that the return value is one which the external class calling the method can handle
Screenshot of my Game class, which is able to call get_move irrespective of whether the current player is human or is a computer.

Learning about these four areas has already highlighted many ways in which my application could be better coded. It’s also removed some of the anxiety when I hear these terms used. My next step is to implement this learning within my application. I’m prepared for many breaking tests whilst I do this!!

(Note: further explanations about what objects and classes are have been omitted).

Siobhan Baker

Written by

Apprentice at 8th Light | Speaker | Advocate for leadership routes for black tech/arts professionals, and for bringing the Arts to STEM | bit.ly/sbakerGitHub

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade