How to Write Awesome code – Ruby on Rails – V1

Everyone wish to write the better code, But How, Is there any pattern / behaviour? Can anyone point to your code what can be improved?

The answer is YES, There is pattern you should follow the OOPS way by it’s core. I have written SOLID principle before this, Please check this too. I am going to write more series in this topic as it’s really interesting and helpful to everyone.

focus on one thing
Focus on one thing in a Class

In Ruby it’s damn easy but we don’t understand what mistake we are doing, so chances of improvement is less. So, Let’s take an example of below MyClass in code1 as below.

Code1:

class MyClass
  attr_reader :input
  
  def initialize(input)
    @input = input
  end

  def double
    case input
      when Numeric  then target . 2 
      when String   then target.next    # lazy fail ex
      when Array    then target.collect {|o| MyClass.new(o).double}
      else
        raise “don’t know how to double #{input.class} #{input.inspect}”
    end
  end
end

The result of the code1 is Output1 as below:

>> MyClass.new(‘aaa’).double
=> "aab"
>> MyClass.new(49).double
=> 98
>> MyClass.new([‘b’, 6]).double
=> ["c", 12]
>> MyClass.new({‘x’=>‘y’}).double
RuntimeError: don’t know how to double Hash {"x"=>"y"}
        from (irb):73:in `double'
        from (irb):80
        from :0

The above code looks Good, Right ?

You’re probably used / gone thought with this pattern. and, Its everywhere in Ruby on Rails.

But, This style of code is Absolutely Wrong and that you should do a different thing.

Why is the problem in above code?

If I change how double works on any of these classes, MyClass must change, but that’s not the real problem. What happens if MyClass wants to double some new kind of object? I have to go into MyClass and add a new branch to the case statement. How annoying is that?

But that’s the least of it. If I’m writing code that follows this pattern, I likely have many classes that do stuff based on the classes of their collaborators. My entire application behaves this way. Every time I add a new collaborating object I have to go change code everywhere. Each subsequent change makes things worse. My application is a teetering house of cards and eventually it will come tumbling down.

Also, what if some other wishes to use MyClass with their new SuperDuperClass object? They can’t reuse MyClass without changing it since MyClass has a very limited notion of what kinds of objects can be doubled.

MyClass is both rigid and closed for extension.

Code2:

class Numeric
  def double
    self * 2
  end
end

class String
  def double
    self.next 
  end
end

class Array
  def double
    collect {|e| e.double}
  end
end

class Object
  def double
    raise "don't know how to double #{self.class} #{self.inspect}"
  end
end

class MyClass
  attr_reader :input
  
  def initialize(input)
    @input = input
  end

  def double
    input.double
  end
end

What basically it should be as below (Something like this) :

Using this new code, Output1 will be the same as before, but now we can also use this way as output2:

Output2:

>> 'aaa'.double
=> "aab"
>> 49.double
=> 98
>> ['b', 6].double
=> ["c", 12]

In this above code2,

The Objects are what they are and because of that, they actually behave the way they do.

Above statement is very simple but incredibly important.

They tell each other what, not how.

What is the event/notification/request and it is the responsibility of the sender.(sender should know).

How is the behaviour/implementation and it should be completely hidden in the receiver (sender not required to know).

Therefore:

  • MyClass should not know how Numeric implements double.
  • MyClass should just ask target to double itself.
  • All possible targets must implement double.
  • Object should implement double to help you get your Ducks in a row.

Thanks for reading. For more information I highly recommend to read Sandi Metz books.

Courtesy 99 Bottles of OOP by Sandi Metz.

Sandi metz

Happy Learning & Coding!

Note: This concept can be used with any programming language.

SOLID Principle in Ruby on Rails

SOLID Stands for:

  • Single responsibility principle
  • Open/closed principle
  • Liskov substitution principle
  • Interface segregation principle
  • Dependency inversion principle

The acronym stands for principles to help software engineers to create / write maintainable source code for long-term that use an OOPS language.

Single responsibility principle: Focus on One thing in a Class Seprately.

Open/closed principle: Class should be open for extension and closed for modification.

 Liskov substitution principle: The derived class must be capable of being substituted by its base class.

Interface Segregation principle:

Interface Segregation Principle (1996)

Dependency Inversion principle (DIP): Now a days used with Docker, Docker composer.

The Dependency Inversion Principle