Browse DevX
Sign up for e-mail newsletters from DevX


Ruby—A Diamond of a Programming Language? : Page 7

Ruby is an object-oriented, meta-programming language that has many developers wondering if there are actually better alternatives to languages like Java and C#.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Modules and Mixins vs. Multiple Inheritance
Some programming languages (C++ and CLOS) offer multiple inheritance: a class inheriting from more than one superclass. For example, a House might inherit from both a Building class (along with Office and Hospital classes) and Residence class (along with Apartment). Without getting into the religious arguments that have occurred in programming language research arenas, let's just say that multiple inheritance can be a very powerful feature, but increases complexity and ambiguity in a language and is not incorporated in many object-oriented languages.

Ruby provides single inheritance. However, it also offers "mixins" which provide many of the features of multiple inheritance. A mixin is a type of "module"...so you first must understand what a module is in Ruby.

A module is a means to group together methods and constants. It is similar to a class, but a module has no instances and has no subclasses. Perhaps the best way to explain a module is to define one as an example. Suppose you were developing a manufacturing application. The application needed access to a great number of general scientific equations/formulas and constants. You could either create a generic class to put these in or you could create a module. The module has the benefit of never having any instances to mess around with to get access to the formulas.

module Formulas ACCELERATION = 9.8 LIGHTSPEED = 299792458 def energy (mass) mass*(LIGHTSPEED**2) end def force (mass) mass*ACCELERATION end end

Now these formula methods and constants can be used by any number of other classes or just by themselves:

irb(main):046:0> Formulas.force(10) => 98.0 irb(main):047:0> Formulas::ACCELERATION => 9.8

Note that to call on a module method or use a module constant, you must use notation similar to the notation used to call on a class method. To call on the module method, you need to use the module class name followed by a dot and then the module method name. For referencing the module constant; use the module name followed by two colons followed by the constant name.

Beyond their usefulness as method and constant "catch-alls," modules also help define a multiple inheritance like facility through mixins. A mixin is simply a module "included" with a class definition. When a class includes a module, all the module's methods and constants become instance methods and contants of the class. For example, suppose that the Formula module defined above was added as a mixin to the Rectangle class. To do this, you would use the "include" keyword:

class Rectangle include Formulas end

Now, instances of Rectangle have the force and energy methods that they can use and the Rectangle class has access to ACCELERATION and LIGHTSPEED constants:

irb(main):044:0> class Rectangle irb(main):045:1> include Formulas irb(main):046:1> end => Rectangle irb(main):047:0> Rectangle.new(4,5).force(10) => 98.0 irb(main):048:0> Rectangle::LIGHTSPEED => 299792458

This means that mixins give Ruby classes many of the benefits of multiple inheritance—but without the issues associated with multiple inheritance.

Control Flow
Like all programming languages, Ruby offers a set of control flow commands to include conditionals (if/else structures), case statements, and loops (do, while, and for), as well as offering exception handling like languages such as Ada and Java. Below are some examples of Ruby's general control flow statements:

if area > 100 "big" else "small" end case height | when 1 | print "stubby\n" | when 2..10 #height in the rage of 2 to 10 | print "short\n" | when 10..20 #height in the rage of 2 to 10 | print "tall\n" | end aRect = Rectangle.new(4,6) while aRect.area < 100 and aRect.height < 10 aRect.doubleSize() end for element in [2, 9.8, "some string", Math::PI] #loop through a collection of objects print "The type is: " + element.type.to_s + "\n&" end

Control statements are generally straightforward, but as shown in the examples of the case statement and for loop above, there are some neat wrinkles that Ruby has thrown in based on other languages and its object-oriented nature.

Exception handling is similar to that in Java with "try...catch...finally" statements. In Ruby, these are "begin...rescue...ensure" statements:

begin # do something rescue ## handle all errors or you can handle explicit errors ensure # do some clean up, like closing an open file. end

To cause an exception in your code, you simply call on the raise method:

if area < 0 raise else if area > 0 and area < 10 raise "Rectangle too small" else if area > 100 raise TooBigException "Rectangle too big" end

The first raise call creates a RuntimeError. The second creates a RuntimeError with a message. The final raise call creates a new instance of a presumably defined TooBigException error and sets its message appropriately.

Thanks for your registration, follow us on our social networks to keep up-to-date