codemachine

Modules, Classes, Pterosaurs

Pterosaur, bat, and bird wings

Pterosaurs, bats, and birds can/could all fly, but each evolved the ability independent of the others. This is an example of convergent evolution, the “independent evolution of similar features in species of different lineages”. The wings of bats, pterosaurs, and birds, the body-plans of marsupials and mammals, and the eyes of vertebrates and cephalopods are just some examples of this.

In Ruby, classes allow objects to be arranged into a hierarchical lineage, using inheritence to pass features from a parent class to all it’s descendants. But we often want to share functionality across unrelated classes. Modules allow us to do just that. Let’s consider the case of pterosaurs and bats.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
module Flyable
  def fly
    puts "A #{self.class} is flying."
  end
end

class Bat
  include Flyable

  def echolocate
    puts "A #{self.class} can see in the dark."
  end
end

class Pterosaur
  include Flyable
end

bert = Bat.new
bert.fly
# => "A Bat is flying."

phil = Pterosaur.new
phil.fly
# => "A Pterosaur is flying."

Contrast this with sharing functionality via classes. Let’s create a couple species of bat by defining descendants of the Bat class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class SeminoleBat < Bat
  def migrate
    puts "A #{self.class} is migrating."
  end
end

class CanyonBat < Bat
  def hibernate
    puts "A #{self.class} hibernating."
  end
end

sam = SeminoleBat.new
sam.fly
# => "A SeminoleBat is flying."
sam.echolocate
# => "A SeminoleBat can see in the dark."

candice = CanyonBat.new
candice.fly
# => "A CanyonBat is flying."
candice.echolocate
# => "A CanyonBat can see in the dark."

Both sam and candice can echolocate and fly, since the SeminoleBat and CanyonBat classes are descendants of the Bat class. The SeminoleBat and CanyonBat classes also each have unique functionality that distinguishes them, and is not shared between their two classes. Only instances of SeminoleBat can migrate, and only those of CanyonBat can hibernate.

1
2
3
4
5
6
7
8
9
sam.migrate
# => "A SeminoleBat is migrating."
sam.hibernate
# => NoMethodError: undefined method `hibernate' for #<SeminoleBat:0x007f88fe006228>

candice.hibernate
# => "A CanyonBat is hibernating."
candice.migrate
# => NoMethodError: undefined method `migrate' for #<CanyonBat:0x007f88fe807c38>

And while Pterosaur, like Bat and all its descendants, can fly, it can’t echolocate, migrate, or hibernate.

1
2
3
4
5
6
phil.echolocate
# => NoMethodError: undefined method `echolocate' for #<Pterosaur:0x007fdcbc082eb0>
phil.migrate
# => NoMethodError: undefined method `migrate' for #<Pterosaur:0x007fdcbc082eb0>
phil.hibernate
# => NoMethodError: undefined method `hibernate' for #<Pterosaur:0x007fdcbc082eb0>

There is a bit more to Modules (they can be used to define class methods as well as instance methods, and they can include constants), but the general purpose they serve is to enable sharing functionality across unrelated classes.

Comments