CSC/ECE 517 Fall 2007/wiki1b 1 as: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 66: Line 66:


== Examples ==
== Examples ==
It’s the example of how to use namespace.
module Grouchy
def Grouchy.say_hello(string='somebody')
  puts "#{string} says: Don't tell me what to do!"
end
end
Grouchy.say_hello is the class method of the module Grouchy
We have a class Person which includes the module Grouchy
class Person
  require "grouchy"
  attr_accessor :name
  def initialize(name='somebody')
    @name = name
  end
end
person = Person.new('Charlie')
Grouchy.say_hello(person.name)
It products:
Charlie says: Don't tell me what to do!
When facing the name conflicts problem, we can use namespace to tell the different of two methods.
module Debug
  def Debug.who_am_i
    "Debug"
  end
end
module Burp
  def Burp.who_am_i
    "Burp"
  end
end
class EightTrack
  include Debug
  include Burp
  def who_am_i
    puts Burp.who_am_i
    puts Debug.who_am_i   
  end
end
et = EightTrack.new
et.who_am_i
It products:
Burp
Debug
Another way is using the alias method, we still have two modules have the same name method who_am_i.
module Debug
  def who_am_i
    "Debug"
  end
end
module Burp
  def who_am_i
    "Burp"
  end
end
class RubyTest 
  include Burp 
  alias :Burp_who_am_i :who_am_i 
  include Debug
  alias :Debug_who_am_i :who_am_i 
end
rt = RubyTest.new
puts rt.Debug_who_am_i
puts rt.Burp_who_am_i
We alias the Burp’s who_am_i method to Burp_who_am_i and Debug’s who_am_i method to Debug_who_am_i so it products:
Debug
Burp
There is another way to do it. We can import a library, ‘use’ package.
  module Foo
      def bar
        "hello"
      end
      def baz
        "world"
      end
  end
  module Test
      def bar
        "goodbye"
      end
      def blah
        "new york"
      end
  end
  class Zap
      use Foo, :bar
      use Test, :blah
  end
  z = Zap.new
  z.bar  # "hello"
  z.baz  # NoMethodError
  z.blah # "new york"
  # Using the new keywords
  class MyKlass
      use Foo :alias => {:bar, :test}
  end
  m = MyKlass.new
  m.test # "hello"
  m.bar  # NoMethodError
It lets us import the method we need rather than whole module.


== Conclusion ==
== Conclusion ==

Revision as of 19:07, 10 October 2007

Background

Ruby does not implement true multiple inheritance, but provides Modules as a way to reuse chunks of codes in many classes.

Modules, unlike like classes in OO languages such as Java, cannot be instantiated or sub-classed. Modules are included in class definitions by using the ‘include’ method which will mix that module’s methods into the calling class. The module’s methods will then become instance methods.

A class can include several modules within the class definition. However, a problem exists when a class includes multiple modules that contain a method of the same name. Since the class will have access to both of these methods, unexpected behavior may occur when the names of the methods conflict.

Let’s look at a few examples!













Question 1: If multiple methods with the same name are defined, there needs to be some way of determining which method a call refers to. The general rule is given on p. 123 of Programming Ruby. But questions still remain.



I.) Is it possible to get unexpected behavior if one of the modules you are using is "enhanced" to contain a new method that happens to conflict with a name of an existing method?

Yes, it is possible to get unexpected behavior if the user does not use the qualified name when calling the ambiguous method. Ruby will first search the last module included, and continue in a descending order. The method that is invoked may not be the method that the caller expected to run.

Source: Programming Ruby textbook


II.) Is it possible to refer to these methods using a qualified name?

Yes, by adding ‘require’ statements for the modules being used, and invoking the methods with the qualified name (ModuleName:method), conflict can be avoided.

Source: http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html


III.) Is it possible to use method aliasing to resolve the ambiguity?

Yes, a library is available to allow a class to selectively mixin methods from a module, as opposed to all of the module’s methods, and alias them on the fly to avoid conflicts.

Source: http://rubyforge.org/docman/view.php/735/309/readme.html

IV.) What approach does good o-o design dictate?

Calling the modules’ methods using qualified names is the better OO design approach. With the alias approach, the inheritance is limited and the programmer will need constantly update the list of aliases for methods needed as they come up.

However, inheriting from the entire module and using the qualified names to avoid conflicts is a more efficient OO approach. It is also easier to read and maintain because in viewing the call, a reader will immediately gather the expected behavior of the call, as opposed to finding the alias definition and trying to make the connection.

Examples

It’s the example of how to use namespace.

module Grouchy

def Grouchy.say_hello(string='somebody')
 puts "#{string} says: Don't tell me what to do!" 
end

end

Grouchy.say_hello is the class method of the module Grouchy We have a class Person which includes the module Grouchy

class Person

 require "grouchy" 
 attr_accessor :name
 def initialize(name='somebody')
   @name = name
 end

end person = Person.new('Charlie') Grouchy.say_hello(person.name)

It products:

Charlie says: Don't tell me what to do!

When facing the name conflicts problem, we can use namespace to tell the different of two methods.

module Debug

 def Debug.who_am_i
   "Debug"
 end

end

module Burp

 def Burp.who_am_i
   "Burp"
 end

end

class EightTrack

 include Debug
 include Burp
 def who_am_i
   puts Burp.who_am_i
   puts Debug.who_am_i    
 end

end

et = EightTrack.new

et.who_am_i

It products:

Burp Debug

Another way is using the alias method, we still have two modules have the same name method who_am_i. module Debug

 def who_am_i
   "Debug"
 end

end

module Burp

 def who_am_i
   "Burp"
 end

end

class RubyTest

 include Burp  
 alias :Burp_who_am_i :who_am_i   
 include Debug
 alias :Debug_who_am_i :who_am_i   

end

rt = RubyTest.new puts rt.Debug_who_am_i puts rt.Burp_who_am_i

We alias the Burp’s who_am_i method to Burp_who_am_i and Debug’s who_am_i method to Debug_who_am_i so it products:

Debug Burp

There is another way to do it. We can import a library, ‘use’ package.

  module Foo
     def bar
        "hello"
     end
     def baz
        "world"
     end
  end
  module Test
     def bar
        "goodbye"
     end
     def blah
        "new york"
     end
  end
  class Zap
     use Foo, :bar
     use Test, :blah
  end
  z = Zap.new
  z.bar  # "hello"
  z.baz  # NoMethodError
  z.blah # "new york"
  # Using the new keywords
  class MyKlass
     use Foo :alias => {:bar, :test}
  end
  m = MyKlass.new
  m.test # "hello"
  m.bar  # NoMethodError

It lets us import the method we need rather than whole module.

Conclusion

As illustrated in the examples, there are at least two ways to ensure that your class runs as expected even when using modules with method name conflicts.

The two approaches are:

  1. calling the module's methods using qualified names
  2. using an alias

In our opinion, the first approach, calling the module's methods using qualified names, is the better approach. Inheriting from the module and using the qualified names to avoid conflicts is a more efficient OO design. It is also easier to read and maintain because in viewing the call, a reader will immediately gather the expected behavior of the call, as opposed to finding the alias definition and trying to make the connection.

With the alias approach, the inheritance is limited and the programmer will need to constantly update the list of aliases for methods needed as they arise.

References

  1. Modules, Mixins, and Inheritance
  2. Ruby User's Guide