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.
ACCELERATION = 9.8
LIGHTSPEED = 299792458
def energy (mass)
def force (mass)
Now these formula methods and constants can be used by any number of other classes or just by themselves:
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:
Now, instances of Rectangle
have the force and energy methods that they can use and the Rectangle
class has access to ACCELERATION
irb(main):044:0> class Rectangle
irb(main):045:1> include Formulas
This means that mixins give Ruby classes many of the benefits of multiple inheritancebut without the issues associated with multiple inheritance.
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
| 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"
aRect = Rectangle.new(4,6)
while aRect.area < 100 and aRect.height < 10
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&"
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:
# do something
## handle all errors or you can handle explicit errors
# do some clean up, like closing an open file.
To cause an exception in your code, you simply call on the raise method:
if area < 0
else if area > 0 and area < 10
raise "Rectangle too small"
else if area > 100
raise TooBigException "Rectangle too big"
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.