CSC/ECE 517 Fall 2007/wiki1 6 b2
Assignment 1 - Topic 6 (MIXINS)
Compare the use of Ruby mixins with how one would solve the same problem in Java or C++. In Java, you might use interfaces or the decorator pattern (p. 91 of Head-First Design Patterns). In C++, you would probably use multiple inheritance. Give code in all three languages and compare the solutions on the basis of verbosity and elegance.
Definition of MIXINS
In object-oriented programming languages, a mixin is a class that provides a certain functionality to be inherited by a subclass, but is not meant to stand alone. Inheriting from a mixin is not a form of specialization but is rather a means to collect functionality. A subclass may even choose to inherit most or all of its functionality by inheriting from one or more mixins through multiple inheritance.
A mixin can defer definition and binding of methods until runtime, though attributes and instantiation parameters are still defined at compile time. This differs from the most widely-used approach, which originated in the programming language Simula, of defining all attributes, methods and initialization at compile time.
Mixins were first used in Flavors, which was an approach to object-orientation used in Lisp Machine Lisp.
Real World Problem
For all the people at NC state, we classify them to three classifications(i.e., faulty, staff, and student). In each of three classes, there is a "say" method to show their status (e.g., I am a student!). However, for students who work part time on campus as staff, they will have a status as both staff and student.
In c++, we use a subclass workstudy which inherits from staff and student to represent such work-study students. The object of workstudy can call "say" methods of staff and student.
In Java, we may define "say" method as interface in staff and student and then implement them in class WorkStudy.
In Ruby, we use mixins by grouping two "say" methods saySta (from staff) and sayStu (from student) together in module Say. After include this module in workstudy class, we can then call saySta and sayStu from any objects of workstudy.
C++ code and output
C++ code
class staff{ public: string getSta() {return Sta_name;} void setSta(string a) {Sta_name=a;} void saySta() {cout<<getSta()<<" is working here!"<<endl;} private: string Sta_name; }; class Student{ public: string getStu() {return Stu_name;} void setStu(string u) {Stu_name=u;} void sayStu() {cout<<getStu()<<" is studying here!"<<endl;} private: string Stu_name; }; class Workstudy: public Student, public staff{ public: string get() {return name;} void set(string w) {name= w; setSta(name); setStu(name);} void say() {staff.saySta(); Student.sayStu(); cout<<"So, "<<get()<<" is a work-study student!"<<endl;} private: string name; }; int main(){ Workstudy* a=new Workstudy(); a->set("Moussa"); a->say(); return 0; }
C++ Output
>>Moussa is working here! >>Moussa is studying here! >>So, Moussa is a work-study student! >>
Java Code and output
Java Code
[[staff]].java public interface [[staff]] { void saySta(); } Student.java public interface student { void sayStu(); } WorkStudy.java public class WorkStudy implements [[staff]], student{ private String name; WorkStudy(String name){ this.name=name; } public void saySta(){ System.out.println(name+" is working here!"); } public void sayStu(){ System.out.println(name+" is studying here!"); } public void say(){ saySta(); sayStu(); System.out.println("So, "+name+" is work-study student here!"); } } mainclass.java public class mainclass { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub WorkStudy j; j=new WorkStudy("Ying"); j.say(); } }
Java output
Ying is working here! Ying is studying here! So, Ying is work-study student here!
Ruby code and output
Ruby code
module Say def saySta(name) @name=name puts “#{@name} is a working here!” end def sayStu(name) @name=name puts “#{@name} is studying here!” end end class Workstudy include Say def initialize(name) @name=name end def say saySta(@name) sayStu(@name) puts “So, #{@name} is a work-study student here!” end end c=Workstudy.new(“Ying”) c.say()
Ruby output
Output: >>Ying is working here! >>Ying is studying here! >>So, Ying is a work-study student!
Discussion
In Java and C++, in order to use say methods, Workstudy has to inherit both Staff and Student as an entity. However, in ruby, a module can collect a set of functionalities wherever these functionalities locate in the class hierarchy. Imagine there are many more classifications in NC state besides Student and Staff. We can collect all say methods from each classification to form a Say module and include this module in a the subclass instead of inheriting all the classifications. Moreover, a subclass may even choose to inherit most or all of its functionality by inheriting from one or more mixins. This advantage provides the subclass huge flexibility to inherit functionality from outside.
Comparision with C++
- Inheritance, in general, is a rigid and hierarchical mechanism, and Ruby mixins is much more flexible than multiple inheritance in c++.
- In Ruby, the same methods can be called to any number of classes regardless of where they are in the inheritance hierarchy
- Modules in Ruby can group together methods and constants which are from different class and modules can also group classes.
- There is a “dreaded diamond” problem in c++ when you try to use multiple inheritance.
Comparision with Java
Comparing with java, mixins has two advantages.
- Firstly, mixins is like a “mix” of interface and abstract class. Interface is designed in Java to perform the act similar to “multiple inheritance” in C++. It is so purely abstract that either any implement of method or instant variable is not allowed. Meanwhile, abstract class may contain abstract method and non-abstract method but one class can only inherited from one super abstract class. Mixin here provides a more flexible way between them. We may implement methods in module or in class inherited from the module to reduce the code redundancy.
- Secondly, the namespace provided by module helps dealing with the situation that a class inherits two methods from different modules with the same name. In Java, such act will throw out an error message.
Conclusion
In RUBY, 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 or C++ Namespace implementation, mixin modules can include the implementation of the methods along with their definitions.
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.
See also
[1] http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.8
[2] http://www.juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/
[3] http://en.wikipedia.org/wiki/Ruby_%28programming_language%29
[4] http://tech.rufy.com/2006/06/ruby-vs-java-matter-of-taste_09.html
References
[1] http://search.cpan.org/~mschwern/mixin-0.06/lib/mixin.pm
[2] http://www.akropolix.net/rik0/blogs/category/programming/ruby/
[3] http://www.cubik.org/mirrors/taligent/Docs/books/WM/WM_31.html
[4] http://vladimirlevin.blogspot.com/2006/05/mixins-interfaces-and-multiple.html
[5] http://www.ruby-doc.org/docs/ProgrammingRuby/