CSC/ECE 517 Fall 2007/wiki1 7 c9

From Expertiza_Wiki
Jump to navigation Jump to search

Mixins versus Multiple Inheritance

Inheritance is the concept of deriving methods from another class, typically the deriving class being called the child class and the class that is derived from is called the parent class. If a language allows a class (child) to derive from multiple parents then it supports multiple inheritance. Ex: C++, CLOS. But many static languages like Java, C# do not allow multiple inheritance instead they simulate it with the concept of interfaces. Dynamic languages like Ruby, Python provide Mixins for the same.

Concept to explore

Multiple inheritance is a controversial concept. Detractors say it leads to messy class hierarchies, it is impossible to implement efficiently, and when the same method is inherited along two different paths, there is no good way to decide automatically which definition should be used. Do mixins solve all of these problems? Give (or cite) examples (e.g., Java, C++, and/or Ruby) to illustrate your conclusions. Are mixins a clear advance over interfaces? Do mixins have any disadvantages not shared by multiple inheritance or interfaces?

Definitions

Inheritance

The process of having a class derive or inherit functionality of other class is called inheritance. This concept offers reusabilty of code and is very useful to represent real world relationships . Depending on the number of parent classes, we can classify single and multiple inheritance.

Multiple Inheritance

This is the situation of a child class deriving functionality from multiple ( more than one ) parent classes . For example a Bike needs to derive functionality from both the " Two wheeler " class and "Motor Vehicle" class simultaneously. This could lead to ambiguity in some cases . When class B and class C are children of class A and a class D inherits from both B and C, then ambiguity arises when D needs to access A's method. Compilers of many object oriented languages usually are unable to determine which version ( the path from B or the path from C) of the method needs to be considered. This problem is also called as "deadly diamond of death".

Interfaces in Java

Interfaces in Java provide the template for the classes implementing it . Each class implementing it needs to provide the implementation of the method which is defined in the Interface. They are envisioned to provide multiple inheritance without the problems that are associated with providing it.

Modules

Modules provide a way of grouping of objects under a single name . The objects may be constants, methods, classes or other modules . Modules provide two benefits

  • Modules provide a namespace and prevent name clashes
  • Modules implement the mixin facility ( incorporating module's content into a class)

A module can be thought of a noun like construct as well as an adjective like construct available for modeling. The difference between a module and class is that modules cannot be instantiated while a class can be. However we can 'include' the module within a class definition ( using 'include if module is in same file or 'require if it is in different file) Module usually contains methods called "module methods" whose names are prefixed with the module name.

Mixin

The other usage of modules other than Namespace implementation is the Mixin. Mixin provides a wonderfully controlled way of adding functionality to the classes. A mixin is a module defined to be included into the definition of a class which would like to include its functionality. Unlike Java interfaces, mixin modules can include the implementation of the methods along with their definitions.

There is some confusion in what exactly a mixin is- whether it is a concept like multiple inheritance or if it is an entity like the module or the class which is including the module. It seems to be used in all the contexts but thinking it as a concept and calling the entities modules and classes makes our lives simpler.

One major advantage of mixin is that we can simulate Multiple Inheritance, as Ruby in concept allows only single inheritance in terms of classes deriving from other classes. With the required functionality enclosed in a Mixin, the classes can inherit(misnomer, correct would be include ) from any number of mixins. The problem of the ambiguity of the method choosing typical of a DDD (deadly diamond of death), is solved in Ruby in that the method with the same name that was most recently included would be considered. Mixins generally do not implement instance variable specific to it, but would prefer to derive it from the class object it is associated with.

In Matz's own words "In fact, a mix-in is actually a strict way of using multiple inheritance. So in LISP, it's a style of using multiple inheritance. In Ruby, we force you to use mix-ins by supporting classes and modules".

Real World Problem

Let us see a real world example and see often why some sort of of multiple inheritance is required. We then give a Object oriented description of the problem followed by an attempt to show how various language solve this example.

Consider a part-time student at an university, this means that he is both a student as well as an employee(typically). A student studies and employee works. But in first place he is a also person. So, he will have attributes and states of both a student and also of an employee. Since these two classifications first belong to the classification person, part-time student also gets any attributes from the person as well. Lets consider some essential features of a person he eats, he will have fun .
Now, the fun a student will have will be different from an employee, so we should distinguish that. So we need some sort of specialization. Now the fun a part time student(if any!) might change. This is the crux of the problem all the languages have attempted to solve and each of them have used a different strategy.

In object oriented terms, we can say that part-time student class inherits both from the student as well the employee. Both student and employee inherit from the class person. The person will have methods eat(),fun() and int i (this is represent the state). Student class will derive the state and method fun() from person. The same is with employee. Note that here the fun() method implementation is different in these classes. And part-time student will get both the derived class fun and the state.


C++ solution

C++ uses the virtual base classes to achieve the functionality desired. Person will contain two pure virtual functions. In order to ensure that both Student and Employee, which derive from the Person class, deal with the same sub-object, we use the virtual base inheritance. In this case , the base class Person is a Virtual base class and the Employee and Student use the keyword "virtual" while deriving Person class. In the part time student class one needs to qualify to know what fun(), it wants to derive but we have eliminated the dilemma of the fun() method in the person class.

// Attempting to polymorphically call a function that is
// multiply inherited from two base classes.
#include <iostream>
using std::cout;
using std::endl;

// class Person definition
class Person
{
public:
virtual void eat() ; // pure virtual
virtual void fun() ; //pure virtual
};


//class Student definition
class Student : virtual public Person
{
public:
// override fun function
void fun ()
{
cout << "Having fun on university campus\n";
}
//override eat() function
void eat()
{
 cout << "Having food on university campus\n";
}
void study()
{
 cout<< "Study\n";
}
};

// class Employee definition
class Employee : virtual public Person
{
public:
// override fun function
void fun ()
{
cout << "Having fun on company campus\n";
}
//override eat function
void eat()
{
 cout << "Having food on company campus\n";
}
void works()
{
  cout<< "Work";
}
};

// class Part_time_student definition
class Part_time_student : public Employee, public Student
{
public:
// qualify which version of function fun
void print() const
{
Employee::fun();
}
};

int main()
{
Part_time_student both; // instantiate Part_time_student object
Employee one;           // instantiate Employee object
Student two;            // instantiate Student object
Person *array[ 3 ];     // create array of base-class ie Person pointers

array[ 0 ] = &both; // valid because of the virtual base inheritance which causes only one sub-object to be created.
array[ 1 ] = &one;
array[ 2 ] = &two;

// polymorphically invoke fun
for ( int i = 0; i < 3; i++ )
array[ i ] -> fun();

return 0;
}


Java's solution

Java makes use of the concept of the interfaces in order to simulate Multiple Inheritance, which otherwise is not valid in Java. Interfaces impose the rule on the classes implementing it of defining the functions declared in the interface.

abstract interface Person {
    void fun();
    void eat();
}
interface Student_inter extends Person{
    void fun();
    void eat();
    void study();
}

interface Employee_inter extends Person{
    void fun();
    void eat();
    void works();
}

interface PartTimeStudent_inter extends Student_inter,Employee_inter {
}

class Student implements Student_inter {
    
    public void fun(){
        System.out.println("Having fun in the university ");
    }
    public void eat(){
        System.out.println("Having food in the univeristy ");    
    }
    public void study(){
        System.out.println("study");
    }
}

class Employee implements Employee_inter {
    
    public void fun(){
        System.out.println("Having fun in the company ");
    }
    public void eat(){
        System.out.println("Having food in the company");    
    }
    public void works(){
        System.out.println("work");
    }
}
class PartTimeStudent implements PartTimeStudent_inter  {
    
    Student stu =     new Student;
    Employee emp = new Employee;
    
    public void fun(){
        stu.fun();
        emp.fun();
    }
    public void eat(){
        stu.eat();
        emp.eat();
    }
    public void study(){
        stu.study();
    }
    public void works(){
        emp.works();
    }
}

class Multiple_inheritance {
    public static void main(){
        PartTimeStudent pts = new PartTimeStudent();
        pts.eat();
    }
}

The implementation involved creation of 3 interfaces along with 3 classes that implement them in order to simulate multiple inheritance; which obviously makes the code much verbose.

Ruby's Solution

The concept of Mixin are used in order to simulate multiple inheritance which is not allowed by Ruby language. Since Mixins include implementation of functions and there is no restriction on the number of mixins that a class can 'include' , the simulation is pretty clear and intuitive. This is apropos with the philosophy of Ruby to be programmer's friendly .

module Person
  def eat
    # does something
  end
  def fun
    # does something
  end
end

module Student
include Person
  def eat
    print " Having food in university campus"
  end
  def fun
    print "Having fun in university campus"
  end
  def study
    print "study"
  end
end

module Employee
include Person
  def eat
    print " Having food in company campus"
  end
  def fun
    print "Having fun in company campus"
  end
  def works
    print "work"
  end
end

class PartTimeStudent
include Student
include Employee
end

pts = PartTimeStudent.new();
pts.study();
pts.works();
pts.fun();

Since Employee was the last module that was included , its fun() function was considered and the output we get is " Having fun in Campus".

Comparision

Mixins vs Java’s interfaces

Advantages of mixins

  1. Interfaces provide only the templates for the actual methods; they do not have the implementation. Though this can be an advantage in some respect, it might cause problems for maintainability. Java’s forces all the classes implementing the interface to also implement the methods. If two classes have exactly same functionality for a method, we just have to replicate the code at two places. So, in future if the method needs to change, we need to make changes at two places. Ruby avoids this by providing implementation in the modules. All the classes that mixes-in this module get the same methods and all the classes get same functionality even if the definition of the method changes at runtime.
  2. One more disadvantage of interfaces is that if a class implements multiple interfaces, Java forces to implement all the methods in those interfaces, making the code unnecessarily large without adding any functionality. With mixins you just include the modules into a class and all the instances of this class will get the methods of the included instances.
  3. Also code becomes much less verbose when mixins are used.

Advantage of Java's interface

One advantage of Java’s interface that it makes the code more explicit with the entire implementation for one method can be found at one place. For Mixin , the code corresponding to a method could be in a completely different file from the class that includes it.

Mixins vs C++ Multiple Inheritance

Advantages of Mixins

  1. Avoids the complex mechanisms of virtual base classes and provides an easy intuitive way of deriving functionality from other modules.
  2. Also code becomes much less verbose when mixins are used.

Advantages of C++ Multiple Inheritance

Though complex, C++ somehow solved the problem of instance variables being carried down from grandparent, through parent to child through virtual inheritance. Whilst in ruby if we have variable like that, we can expect random results and hard to diagnose errors.

References:

  1. Programming Ruby: The programmatic programmer’s guide
  2. Deadly diamond of death by Robert Martin
  3. An article by Jared Carroll on multiple inheritance
  4. Wiki entry on Ruby
  5. The C++ code snipped has been adapted from this site
  6. The thread on sun forums which was used to adapted to form the example


See Also

  1. Article from O'Reilly's website which discusses mixins at depth
  2. One more blog on mixins, modules and inheritance