CSC/ECE 517 Fall 2011/ch2 2c ac

From Expertiza_Wiki
Jump to navigation Jump to search

Mixin versus Interface

Introduction

[To be changed] Functionality such as Comparable is done in Java with interfaces and in Ruby with mixins. Is there an advantage in using one or another? Consider other behaviors achieved with mixins in Ruby: Singleton, Enumerable, and DataMapper, for example. Could you accomplish these with interfaces in Java? Does this mean that mixins are more powerful? Or can interfaces achieve some purposes that mixins can't?

Definition

Multiple Inheritance

Multiple inheritance is a feature of some object-oriented computer programming languages in which a class can inherit behaviors and features from more than one superclass. (From Wikipedia, http://en.wikipedia.org/wiki/Multiple_inheritance)

Inheritance vs Composition

"Inheritance represents an incredibly tight coupling of two components. Change a parent class and you risk breaking the child class. But even worse, if code that uses objects of the child class relies on those objects also having methods defined in the parent, then all that code will brake too." (Thomas 84)

In Ruby, “the include statement makes a reference to a module. If multiple classes include that module they’ll all point to the same thing. If you change the definition of a method within that module, even while your program is running, all classes that include the module will exhibit the new behavior.” (Thomas 79)

Interfaces in Java

http://download.oracle.com/javase/tutorial/java/concepts/interface.html

Modules in Ruby

Modules in Ruby are a way to group together methods, classes and constants. They are similar to namespaces in languages such as C++. (From lecture 6 note)

Mixin using Modules

The most interesting use of modules is to define mixins. When you include a module within a class, all its functionality becomes available to the class. Not only can modules contain class methods; they can also contain instance methods. (From lecture 6 note)

Feature Comparison

Ruby mixins vs Java interfaces feature comparison
Features Ruby Mixins Java Interfaces
Add functionality/behavior to a class Yes. When a Ruby module gets mixed in a Ruby class, the class receives the implementation (behavior) of the methods defined in the module. No. A Java interface "can contain only constants, method signatures, and nested types. There are no method bodies. Interfaces cannot be instantiated—they can only be implemented by classes or extended by other interfaces." (Java Interface)
Favor composition over inheritance Yes. The class that includes a module establishes a "use-a" relationship with the module. This is composition. No. The class that implements an interface establishes a "is-a" relationship with the interface. This is inheritance.
The code of the mixin/interface interacts with the code of the class that includes/implements it Yes. A mixed-in module can implement methods in terms of the host's class methods. See the Enumerable example. No. An interface provides a set of method signatures that the host class must implement. An interface provides no implementation of methods.

Mixin

Mixin Advantage

Mixin can inherit behavior of modules while Interface only inherit method signature.

Mixin Disadvantage

"A mixed-in module's instance variables can clash with the ones of the host class or with the ones of other mixins."(Thomas 82) There are programming practices to avoid this potential problem.

Interfact

Interface Advantage

Method collision is handled more carefully with Interface. The method clashes are eliminated during compile time.

Interface Disadvantage

Examples

We are going to compare mixin and interface implementions by looking at some code examples.

Comparable

Comparable provides a set of operations that can be used to compare two objects.

In Ruby

If a Ruby class wants to use Comparable functions, it needs to define a method called <=> (sometimes called “rocket”). Ones the rocket function is defined, we get a lot of comparison functions for free, such as <, >, <=, >=, == and the method between.


Suppose we have a Rectangle class.

class Rectangle
    attr_reader :x,:y
    def initialize(x,y)
	@x,@y=x,y
    end
end

Now we want to compare the area of two rectangles. We can do so by include the Comparable mixin.

class Rectangle
include Comparable
    def area
	x*y
    end
    def <=>(other)
	self.area<=>other.area
    end
end

The <=> function uses Comparable mixin of Fixnum class to compare the area of two rectangles. We can call the Comparable methods on Rectangle objects.

r1 = Rectangle.new(3,4)
r2 = Rectangle.new(4,5)
puts r1.area
if r1 < r2
   puts "The area of Rectangle 1 is smaller than Rectangle 2"
else if r1 > r2
   puts "The area of Rectangle 1 is larger than Rectangle 2"
else
   puts "The area of Rectangle 1 equals to Rectangle 2"
 end
end


=>12
=>The area of Rectangle 1 is smaller than Rectangle 2

In Java

Java provides Comparable interface. A Java class implements Comparable interface need to define compareTo method.

For the same Rectangle class example, we need to define compareTo method, and define each of the <=> operators within the method.

package classes;

public class Rectangle implements Comparable {

	private int x,y;
	
	public Rectangle(int width, int length)
	{
		x=width;
		y=length;
	}
	
	public int area()
	{
		return x*y;
	}
	public int compareTo(Object otherRectangle)
	{
		/*
		 * If passed object is of type other than Rectangle, throw ClassCastException
		 */
		if(!(otherRectangle instanceof Rectangle)){
			 throw new ClassCastException("Invalid object");
		}
		/*
		 * If left operand less than right operand return -1
		 * If left operand greater than right operand return 1
		 * If left operand equals to right operand return 0
		 */
		int otherArea=((Rectangle)otherRectangle).area();
		if (this.area() < otherArea)
			return -1;
		else if (this.area() > otherArea)
			return 1;
		else
			return 0;
	}
}

compareTo function is then used on Rectangle object when comparing two Rectangle objects.

package classes;

public class CompareRectangle {
	public void main(String args[]){
		
		Rectangle r1=new Rectangle(3,4);		
		Rectangle r2=new Rectangle(4,5);

		if (r1.compareTo(r2) < 0)
			System.out.println("The area of Rectangle 1 is smaller than Rectangle 2");
		if (r1.compareTo(r2) > 0)
			System.out.println("The area of Rectangle 1 is larger than Rectangle 2");
		if (r1.compareTo(r2)==0)
			System.out.println("The area of Rectangle 1 equals to Rectangle 2");
			
	}
}

Singleton

The Singleton design pattern ensures that only one instance of a particular class may be created for the lifetime of a program.

In Ruby

The singleton library contains the Singleton module that if mixed into a class will make the class a singleton. The Singleton module makes the mixee new method private and replaces it with a method called instance that when called returns a singleton instance of the mixee.

You do not have to code the singleton functionality inside each class that needs to be a singleton.

require 'singleton'

class Klass
      include Singleton
end

a,b  = Klass.instance, Klass.instance
=> [#<Klass:0x007fa7a28798e8>, #<Klass:0x007fa7a28798e8>]

a == b
=> true

Klass.new
NoMethodError: private method `new' called for Klass:Class

In Java

Each class that needs to be a singleton must implement the singleton functionality itself such as in the following example.

public class Singleton  {
    private static final Singleton instance = new Singleton();
 
/**
Private constructor prevents instantiation from other classes
**/
    private Singleton() {
    }
 
    public static Singleton getInstance() {
        return instance;
    }
}

Enumerable

In Ruby

In Java

DataMapper

Martin Fowler describes the Data Mapper design pattern as "a layer of software that separates the in-memory objects from the database. Its responsibility is to transfer data between the two and also to isolate them from each other. With Data Mapper the in-memory objects needn't know even that there's a database present; they need no SQL interface code, and certainly no knowledge of the database schema. (The database schema is always ignorant of the objects that use it.)"(Data Mapper)

In Ruby

The DataMapper Ruby library was originally developed to address perceived shortcomings in Ruby on Rails' ActiveRecord library. For example DataMapper lets you avoid writing raw query fragments yourself. It allows you to write

Zoo.all(:name => 'Dallas')

instead of

Zoo.find(:all, :conditions => [ 'name = ?', 'Dallas' ])

In Java

Unique Use Cases

Some use cases can achieve only through mixin, on the other hand, sometimes, interface can achieve some features that cannot be implemented with mixin.

Mixin Only Use Cases

Because of the unbound polymorphism nature, the return type of Ruby method is determined at run time. This allows a class inherits from two modules that include same method call with different return types.

In Java, a class cannot inherits from two interfaces that include same method with different return type, due to the name conflict. For example,

public interface I1 {
	public void method1();
}
public interface I2 {
	public int method1();
}

package classes;
import interfaces.I1;
import interfaces.I2;

public class Mul_inherit implements I1, I2{
...
}

The Mul_inherit class will give error since there are no ways to satisfy both method1 signatures from I1 and I2.

But with Mixin this example is allowed since

    1. A method always returns a value (nil if nothing is defined).

    2. The type of return object is determined at runtime.

module A
    def method1
	#this method returns nothing"
    end
end

module B
    def method1
        "returns a string"
    end
end

class Myclass
include A
include B
end

puts Myclass.new.method1

=>returns a string

In Ruby the latest method definition always overloads the previous one.

Interface Only Use Cases

Reference

Thomas Dave, "Programming Ruby 1.9" textbook

Java Interface

Data Mapper Design Pattern

Data Mapper in Ruby

Data Mapper in Java