Guru on Rails
Last article I introduced S.O.L.I.D in coding. I found that I didn't really dive deep in each principle as I thought. Programming is not about the chain of theories. We need to seek for the situations in order to apply what we learned. We need to rectify every line of code to train our brain, the sense of SOLID.
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification - Bertrand Meyer
Modification? Yes, that's about the modification of existing code. When we put the fingers to write any lines of code, we suppose they will never change. Hence, be careful!
Extension? That is about appending more code, not changes. How do we do that? By using inheritance. If you are Rubyist, we can use both inheritance and module. We don't modify the existing methods, classes and modules. But we do:
- New classes inherit existing classes. Thus, we add extra code to subclasses but not modify superclasses.
- Override existing methods. Thus, we don't make changes on existing methods but we override them.
However, to do those things we need a pattern. That is program to interfaces, not implementation in Ruby. We use interfaces, make it easy for override (ruby doesn't have interfaces but we have implicit interfaces).
For example, firstly we need to develop to manage Exceptions. We have an exception, it means an error occurs.
There're a bunch of exceptional types corresponding with error types. But at the initial project timeline, we manage only Import Error and some other errors in general.
class StandardError < Exception; end class ImportError < StandardError; end
Time by time, the project grew up. We need to develop Export function. Hence, Export Error appears. We don't make changes on existing code. We add a new class for Export Error.
class ExportError < StandardError; end
Furthermore, we can add any Error types we want in future as subclasses of StandardError. We can reuse the methods of it or we can override them. That is Open/Closed Principle.
Moreover, we have Response class for convert to json:
class Response def self.to_json(data) end end Response.to_json("Will Nguyen")
However, time by time the application needs to change the data type. Other classes require plain text type. We don't want to make changes on Response class (closed). But we define other class named PlainTextResponse which inherits Response and add one more method to_text.
class PlainTextResponse < Response def self.to_text(data) end end Response.to_text("Will Nguyen")
We can see that, at the beginning when we made to_json method, that's a mistake in software design. We should use interface instead of implementation - we use "program to interface, not implementation" pattern in ruby. Why? Because interface keeps the meaning of behaviours. Make it easy to implement polymorphism. Obviously, we have to implement new method to_text , if we implement like this, we won't have to add more methods. But we override the existing method.
# Existing code class Response def self.convert(data) end end Response.convert("Will Nguyen") # New code class JsonResponse < Response def self.convert(data) end end class PlainTextResponse < Response def self.convert(data) end end JsonResponse.convert("Will Nguyen") PlainTextResponse.convert("Will Nguyen")
In fact, we might know this principle. But if we don't train our sense to recognize what need to be applied while coding, it goes nothing. We learn to use it, not learn to learn. We might find a lot of rules and see a ton of cases to apply them under very simple code. I presented some design patterns to team members. But you know what? After they heard what I said and they saw what I showed. They went to a conclusion: "That's very simple". Something looks simple but if someone asks us about those things. We can't even say more. "That's simple" and the discussion ended. That sort of thought prevents us from learning and improving. "Simple!" and we stop there. They saw code like that but they don't know how many patterns in code. Why they do that, why they do this. They have no idea. Hence, let's start from simple things.