DRY in Ruby equals inheritance and mixin

Tram Ho

In this article I would like to mention the basic way to minimize code duplication in the application, you will often hear about DRY – Don’t Repeat Yourself.

Inheritance

Ruby is an object- oriented programming language ( OOP ) so it has inheritance , and it is used for classes. In ruby, just declare simply by the character < , and the order is a child < father. See the following simple example.

In the above declaration 3 classes including Animal , and Dog , Cat are 2 direct subclasses of Animal . Then we have the following:

Dog and Cat superclass are both Animal

Cat doesn’t need to define anything but still executes the above statement because Cat inherits all the class method, instance method and its parent variable is Animal class.

Dog also inherits methods and variables from Animal , in addition to the color variable from the parent, it also defines its own variable name . So unlike Cat , Dog has more variables for its object.

In Dog and Animal , there is a speak method, which means that Dog is overwriting the parent’s own method that it inherits, while Cat does not override this method. When calling speak as above, the dog will put content inside the speak method of the Dog class, while for cat it will also take the speak method of the Animal class.

Overwriting the father’s method in the inheritance tree is polymorphismpolymorphism – one of the four important properties of OOP .

super

With its inheritance, Ruby provides you a great method that is super . When you call super at a method in the current class, it will look up the parent classes in the inheritance tree to find the method with the same name as the other method to execute it. See an example to make it easier to imagine:

We see, when calling super in the speak method of the Dog class, it finds and implements the speak method in the Animal class as well.

A familiar way to use super is at initialization with the initialize method as follows:

As you can see above, the Dog class has no initialization for the name attribute, and instead it calls super to find the initialize method in the Animal class. And so after the initialization is complete, the dog has both name and color .

Mixing in the module.

Another common way to DRY code is to use module . Recalling the definition of the module a bit, in Ruby, the module is used to group methods, variables, constants together (when grouping classes, the module keyword now acts as a namespace). We mix the module into the class with the keyword include , prepend or extend .

For example with a simple inheritance tree as follows:

Test run we get the results:

We see reck and nemo can call the swim method and not paws , because only the Dog and Fish classes have the Swimmable module mixin inside it.

Ancestors method

Above are 2 ways to DRY code. In this section, I would like to introduce you a little trick so that when you call a method from a class or from a specific instance of that class, the ancestors method.

Suppose you have a inherited mixin pattern as follows:

We see when calling the swim method from the instance of the Fish class, it will call the swim method in the same class. Although both Animal and Swimmable also have this method, it is the override mentioned above. But what it is essentially, call the ancestors method to see what it returns you.

The ancestors method will return a list of all ancestor classes in the inheritance tree and include modules that are mixin inside the tree’s classes ( Kernel is a mixin module in the Object class). And every time a method is called from the class or instance of that class, Ruby will go in turn in order of each class and module according to the results of the ancestors method to search for the called method. As in the above example, the method will be searched in the order Fish -> Swimmable -> Animal -> Object -> Kernel -> BasicObject . Where the swim method is found, we will jump into the implementation and return the result, and the result is the swim method of the Fish .

As a final example, suppose I created a new Pree module and mixin it in the Fish class with the prepend keyword

For this example, the method will be searched in the order Pree -> Fish -> Swimmable -> Animal -> Object -> Kernel -> BasicObject . As you may have noticed, prepend will insert the module first, while include will insert the module immediately after that class.

Inheritance vs Modules

In the final section of this article, I would like to give some comparisons between inheritance and module

  • Ruby does not provide multiple inheritance in the form of a child with multiple fathers, but can mixin freely many modules in the class.
  • Cannot create an instance of module, module only uses group of methods, variables, constants and namespace
  • For is-a relations, use inheritance, ex: Dog is-a Animal . For has-a relation, use mixin, ex: Dog has-a swim ability

Refer

https://launchschool.com/books/oo_ruby/read/inheritance#classinheritance https://www.oreilly.com/learning/ruby-cookbook-modules-and-namespaces


Thank you for following the article.

Share the news now

Source : Viblo