A simple SOLID object-oriented solution example.
Tags: solid, ruby, design, oop
Published: 2020-01-20 19:55:48

---
title: A simple SOLID object-oriented solution example.
published: true
description: Understanding SOLID principles
tags: solid, ruby, design, oop
---

First of all, I write this because I want to improve my knowledge of SOLID principles, I don't completely understand them so I encourage you to make questions or have an opinion.

Let's start solving a problem.

## Requirement
A text editor allows the user to select a layout for the document.
The default layout is one column, but he can choose two-columns.

## Analysis
Under certain conditions, the format needs to be different.
This implies that the format is implemented in many different ways.

## Design
How do we model this with objects?
We could create a `Format` class with `#one-column` and `#two-columns` methods.
If we do that, the `Page` object would need to have two versions.

```ruby
class TwoColsPage
 def render
  ...
  @text = Format.one-column(@text)
  ...
 end
end

class SinglePage
  def render
    ...
    @text = Format.two-columns(@text)
    ...
  end
end
```

The main problem with this is that once you create a single page document you cannot change it to a two-column document.
Also if we need another format we're forced to create a new Page object for that format.

Another option is to rely on polymorphism(multiple classes implementing the same method)
* OneColumnFormat#format
* TwoColumnFormat#format

From the consumer perspective:
```ruby
class Page
  def render
    ...
    @text = FormatterFactory.create(with: :two_columns).format(@text)
    ...
  end
end

class FormatterFactory
  # Returns objects that implement the implicit Formatter interface
  # That is, objects that respond_to :format method
  def self.create(with:)
    case with
    when :one_column
      OneColumnFormatter.new
    when :two_column
      TwoColumnFormatter.new
    end
  end
end
```

### Open-Close
It is open to support new formats by adding new formatters without modifying code.

### Single Responsibility
It does respect *SRP*, every object has a single responsibility.
```
Page --> returns the resulting text after by applying a set of transformations
FormatterFactory --> creates the requested formatters
OneColumnFormatter --> applies the format strategy for one-column text
TwoColumnFormatter --> applies the format strategy for two-column text
```

### Interface Segregation
*ISP* would mean that the consumer object doesn't get useless methods.  The Page (consumer) receives only one method `format` which he uses.

### Liskov Substitution
In this case *LSP* means that objects returned by FactoryFormatter all met the same interface.
If we call `FactoryFormatter.create(:unknown_format)` it returns `nil`.
does `nil` meet the formatter interface?
does `nil` respond to `format`?
NO!
how can we fix that?
```ruby
class FormatterFactory
  def self.create(with:)
    case with
    when :one_column
      OneColumnFormatter.new
    when :two_column
      TwoColumnFormatter.new
    else
      NullFormatter.new
    end
  end

  class NullFormatter
    def format(text)
      text
    end
  end
end

```
Why doing that?
If we return nil for some cases, then the consumer would need to ask for the answer.

```ruby
formatter = FormatterFactory.create(with: format)
if formatter.nil?
  # we're not able to format the text
  Logger.error("Formatter failed creating formatter with format #{format}")
else
  @text = formatter.format(@text)
end

```
Let's suppose that the execution returns a NullFormatter.
```ruby
formatter = FormatterFactory.create(with: format)
@text = formatter.format(@text) # returns the same text it was before
```
In that case instead of handling a failure or getting a `No method :format for nil class`
The failure is avoided and the user gets the text with no format.

### Dependency Inversion
*DIP* Depends on abstractions, not concretions. Well, I think that Page depends on objects that implement `#format` so I think that "something that implements `format`" is the abstraction.

Something that confuses me is that this principle is also called Dependency Injection, in this scenario we could adapt the code to inject the formatter to the page.
```ruby
Page.new(format: FormattersFactory.create(:two-columns))
```
Doing that `Page` wouldn't depend anymore on `FormattersFactory`, but we are moving the dependency to the consumer of `Page` (a previous step in the call stack).

I suppose that this is trying to organize the code in a way were all the creations are at the top level in the call-stack.
why doing that?

If you reached this point, thanks for reading hope your comments!