SOLID: Single Reponsibility Principle in Ruby


Created by Stan on 18-04-2023


The Single Responsibility Principle (SRP) is one of the five principles of object-oriented programming and design known as SOLID. SRP states that a class should have only one reason to change, meaning it should have only one responsibility. Adhering to the Single Responsibility Principle leads to a more maintainable, flexible, and modular codebase. In this article, we will explore the SRP and demonstrate its application in Ruby.

Understanding the Single Responsibility Principle

The SRP revolves around the idea that a class should focus on a single task or responsibility. By limiting a class to a single concern, you can create a codebase that is easier to understand, maintain, and modify. When a class has multiple responsibilities, changes in one area of functionality might inadvertently affect other areas, leading to unintended consequences and increased complexity.

Applying the Single Responsibility Principle in Ruby

Let's illustrate the Single Responsibility Principle using a Ruby example. Imagine we have a system that sends notifications to users through various channels like email and SMS.

We can use the mail gem for sending emails and the twilio-ruby gem for sending SMS messages. First, you'll need to install these gems:

gem install mail twilio-ruby

Here's an initial implementation without considering the SRP:

require 'mail'
require 'twilio-ruby'

class NotificationSender
  def initialize(user, message)
    @user = user
    @message = message
  end

  def send_email(from_email)
    Mail.deliver do
      from    @from_email
      to      user.email
      subject 'Notification'
      body    message
    end
  end

  def send_sms(account_sid, auth_token, from_phone_number)
    client = Twilio::REST::Client.new(account_sid, auth_token)

    client.messages.create(
      from: from_phone_number,
      to: @user.phone_number,
      body: @message
    )
  end
end

The problem with this implementation is that the NotificationSender class has multiple responsibilities, handling both email and SMS notifications. This violates the Single Responsibility Principle.

To refactor this code and adhere to the SRP, we can separate the notification logic into different classes:

class NotificationSender
  def initialize(user, message, notifier)
    @user = user
    @message = message
    @notifier = notifier
  end

  def send_notification
    @notifier.send(@user, @message)
  end
end
require 'mail'

class EmailNotifier

  def initialize(from_email)
    @from_email = from_email
  end

  def send(user, message)
    Mail.deliver do
      from    @from_email
      to      user.email
      subject 'Notification'
      body    message
    end
  end
end
require 'twilio-ruby'

class SmsNotifier
  def initialize(account_sid, auth_token, from_phone_number)
    @client = Twilio::REST::Client.new(account_sid, auth_token)
    @from_phone_number = from_phone_number
  end

  def send(user, message)
    @client.messages.create(
      from: @from_phone_number,
      to: user.phone_number,
      body: message
    )
  end
end

Now, each class has a single responsibility: NotificationSender is responsible for managing notifications, EmailNotifier is responsible for sending email notifications, and SmsNotifier is responsible for sending SMS notifications.

This refactored code adheres to the Single Responsibility Principle, as each class focuses on a single task.

Now you can use the NotificationSender class to send notifications through different channels:

user = OpenStruct.new(email: '[email protected]', phone_number: '+1234567890')
message = 'Hello, this is a notification.'

email_notifier = EmailNotifier.new('[email protected]')
sms_notifier = SmsNotifier.new('<your_twilio_account_sid>', '<your_twilio_auth_token>', '<your_twilio_phone_number>')

notification_sender = NotificationSender.new(user, message, email_notifier)
notification_sender.send_notification

notification_sender = NotificationSender.new(user, message, sms_notifier)
notification_sender.send_notification

The example code above demonstrates how to use the NotificationSender class and the specific notifier instances to send notifications through email and SMS. Note that this is a basic implementation, and you may need to adjust the code depending on your specific requirements.



Related Posts