Single Responsibility Principle for beginners (1/2)
Tags: beginners, programming, ruby
Published: 2023-01-09 13:59:37

When learning Ruby it's almost impossible not to come into contact with the concepts of object-oriented programming (hereafter OOP!).

The maxim that 'everything in Ruby is an object' 🤔 shows just how integral object-oriented design is to the language. For me, it's been a great experience to learn about one as I learn about the other.

I thought a some blogs exploring key tenets of OOP, would help improve me as a Ruby programmer and I hope it's of use to beginners out there or for people looking refresh themselves on the fundamentals of OOP theory 🤓

This blog will explore SRP, how to check if our classes are following it and to think about create classes that are more likely to do so from the get-go!

### What is the Single Responsibility Principle (SRP)? 

- The SRP is one of the [SOLID principles](https://en.wikipedia.org/wiki/SOLID) of Object-Oriented Design 

- It's a guiding convention that a class should only have one responsibility or job

- This is often extrapolated to 'a class should have only one reason to change'

What does this mean in practice? Let's explore the idea with an example...

### Example- A new bookshelf[](url)

_We're the owner of a bookshop and would like to put up a new shelf. We want to design an app that will keep track of the books that go on that shelf._


To make an app that follows the SRP, we'll start by thinking carefully about what classes to create in the first place.


## Choosing which Classes to Create

- A great way to start thinking about what classes need creating is to look at the nouns in our brief

- When considering each noun, ask if it could represent _data_ or _behaviour_ that would help you in your app's goal

- For instance, we might think that a `Bookshop` class would be a good thing to make, but it's unclear how making one would help us in our goal of keeping track of books that go on a particular shelf (if there were many shelves to keep track of, we could return to this idea)

- In our example, if we think about the _data_ and _behaviour_ we need, we'll probably need a `Book` and `Bookshelf` class

- We can use a `Book` class to represent the books we add to the shelf as objects. It will need to be able to create objects that represent our books, that bundle _data_ specific to each book

- We can use a `Bookshelf` class to keep track of the books currently on that shelf. It will need to have the _behaviour_ of saving our book objects to a variable within itself and adding or removing books from that variable

We've decided what nouns we want to represent as classes and what behaviour and data each class is responsible for. 

Our code could end up looking something like this:

```
class Book
  attr_accessor :title, :author
  
  def initialize(title, author)
    @title = title
    @author = author
  end
end

class Bookshelf
  attr_accessor :books
  
  def initialize
    @books = []
  end
  
  def add_book(book)
    books << book
  end
  
  def remove_book(book)
    books.delete(book)
  end
end
```

So far, so good. This code looks sensible enough, but does it follow the Single Responsibility Principle?

How can we be sure either way?

## Checking if a Class has a Single Responsibility 

Not all examples are as simple as the one we're dealing with. It can be necessary for a class to have multiple methods to manage its responsibility and, as a project's complexity grows, it's important to have a way of deciding whether a object's method falls within it's "responsibility". 

For me, the best way that I found to check this, I got from [Sandi Metz's 'Practical Object Oriented Design'](https://www.amazon.co.uk/Practical-Object-Oriented-Design-Ruby-Addison-Wesley/dp/0321721330) (a book I can't recommend enough, if you find this topic interesting!).

The technique goes like this:

- Personify the class in your mind

- Rephrase the method as a question

- Ask yourself if it makes sense to ask _this_ class _this_ question

In our example, our `Book` class is initialised with an 'author' method and a 'title' method.

We could rephrase these methods as _"Hi Book, what's your title?"_ or _"Hi Book, who's your author?"_.

![A cartoon book, smiling with a thumbs up](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3c6avycu6hfzlqgax584.jpeg)

Similarly, we can put the `Bookshelf` class's methods as something like _"Hey Bookshelf, what books are on you right now? Can I add this one? Can I take this one?"_

![A cartoon bookshelf](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mjte4ump2epq0farvdqz.png)

All of these questions seem perfectly valid to ask our personified classes. We can feel safe then, that each of these methods fall within its class's 'responsibility'.


#### A Counter-example

That being said, [a good way to learn to do something right is to do something wrong on purpose.](https://www.newscientist.com/article/mg25333770-400-make-mistakes-on-purpose-it-can-dramatically-boost-your-performance/) With that in mind, let's create a `Book` class that's responsible for everything and see if it passes our test!

Such a class might look like this: 

```
class Book
  attr_accessor :title, :author
  
  @@bookshelf = []
  
  def self.remove_book(book)
    @@bookshelf.delete(book)
  end
  
  def initialize(title, author)
    @title = title
    @author = author
    @@bookshelf << self
  end
end
```


If we were to rephrase the `.bookshelf` method as a question, it might sound like "_Book, what instances of you have been added to the shelf so far?_". The `.remove_book` method might be put something like _"Book, can you remove an instance of yourself from the shelf?"._



![A cartoon book, looking confused](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzf9afl21zmlhg5rfhgt.jpeg)

These are pretty strange, abstract questions to be asking a book (personified or otherwise)... We can intuitively feel that this isn't something that a 'book' should be doing and that's a hint to us that this class has too many responsibilities.

***

## Summing up

#### Choosing initial classes that are likely to follow the SRP
- When deciding on which classes to start with, think about the "objects" (often nouns) in an application's main goal or brief

- Ask yourself, which of these objects can represent _data_ or _behaviour_ that will help achieve your app's goal?

- The objects that occur to you are a good candidates for becoming classes

#### Checking if a class's method falls under its responsibility

- Personify the class and rephrase the method as a question

- If the question seems fair to ask this particular class, it likely falls under its responsibility

***

As a beginner, it may seem strange to read phrases like 'occur to you', 'seems' and 'likely' when reading about something as seemingly technical as programming. There's nothing particularly concrete about whether a question 'feels right' to ask a class. 

However, as I've been reflecting lately, a huge part of developing as a programmer though, is developing your decision making process. [The old joke goes] (https://twitter.com/potetotes/status/1348044775887233024): the key to being a senior developer are the words "it depends".

### Next Time

This blog has focused on the practicals of SRP: how to design classes that are likely to follow it from the ground up and how to check whether a class is doing too much.

But why is Single Responsibility Principle important to our coding decisions? What does it offer our code, or make available to us down the development pipeline? Do our classes _always_ need to have one and only one responsibility? 

These are the questions will be explored in my next blog, I hope you enjoyed this one and keep your eyes peeled til then!