CSC/ECE 517 Fall 2011/ch1 1i zf

From Expertiza_Wiki
Jump to navigation Jump to search

CSC/ECE 517 Fall 2010/ch1 1i zf


Introduction

In Object-oriented languages, method is a subroutine which associates with the instances of the class to define the specific behavior of this class. In the inheritance of these languages, methods reimplementation is essential because descendant class inherited from the ancestor class in which properties and method have been defined to some extent. In fact, method reimplementation not only simplify the coding (become more readable) but also make the relationship tight between ancestor class and descendant class. However, in different OO languages, the ways of acquiring or allowing method reimplementation are significantly different. In this article, we are going to introduce these differences and provide code for better understanding.

Principle of method reimplementation in descendant class

Method reimplementation is various in different OO languages according to their own programming rules and complier environments. And it also associated with inheritance between ancestor class and descendant class. Several terms of method are listed below.

Abstract method

Abstract method is a method only has name but little or no implementation in the ancestor class, which must be also an abstract class. In most of the case, codes in abstract method are considered dummy but it does not mean useless. In fact, it provides a place for descendant class which inherited from the ancestor class to define specific behaviors and actions.

For example, a toy company develops an ancestor class called “animal” which contains “dog”, “bird” and “fish” as its descendant classes. These subclasses have same characteristic such as color, size, price and behavior like move, speak. Here we use the abstract methods for move and speak so that the subclasses can define their own behaviors which act differently.

Virtual method

Virtual method, or virtual function, is the method that first declared or defined in the ancestor class and then completed defined in the descendant class. Based on the functionality in the subclasses, virtual method can be simply defined or overridden with details. In a virtual method in which only has the name but no implementation, we call this as pure virtual method, as known as abstract method introduced above.

Overridden method

Overridden method, as mentioned in virtual method, is used in OO language involving inheritance hierarchy. It allows descendant classes to redefine the method in their ancestor class with more details and particular purposes. “Override” means the implementation in subclasses rewrites or redefines the original implementation of the method in their super class without changing the name, parameter and return type. To better demonstrate how overridden method work, we take the toy company example again. In the behavior aspect of “animal”, we have the method call “move”. In this method, we know it define the same of movement for all the animals. However, the way of movement among “dog”, “bird” and “fish” are different. Thus, in “dog” class, we may redefine the detail in move method as “I use leg”; in “bird” class, the detail of move method is “I use wings”; and in “fish” class, the detail of move method is “I use fin”.

Overloaded method

Overloaded method means that method share the same name with other method but may be different in other aspects such as parameter, number of parameter and data type. And the correct method will be executed automatically during the runtime depends on how it is called.

Method reimplementation in different languages

Smalltalk

In Smalltalk language, we can use the inheritance to reimplementation the methods. Inheritance is very useful in Smalltalk which makes the Smalltalk language reusability and extensibility. There are two ways to modify the behavior of the methods in Smalltalk by inheritance. One is adding new methods and the other is overriding the inherited method.

Adding Methods

Adding new method is very simple; we can add either instance or class methods, to the class definition. The following diagrams show how the adding method works:

ClassRoom’s Methods
Room instance methods
Room_Number
Room_Size
Open_Window
Open_Door
ClassRoom instance methods
Turn_On_Computer
Clean_Blackboard

The ClassRoom class is specific types of Room. The ClassRoom class is inheriting from Room Class, so the new methods Turn_On_Computer and Clean_Blackboard will add to the ClassRoom class. Now ClassRoom class definition can now support all of the messages or methods in Room and the two new messages.

Overridden method

Overriding an inherited method is very important way to provide unique behaviors to a class. If an object receives a message which we have a method in the class definition for that massage, the object will work its way up the hierarchy until it finds a method with that name. We cannot delete the inherited methods, if we do not need the method in the superclass, we can provide a method by the same name with the superclass in the subclass with no code in the method. In this way, we can simply replace the behavior of the superclass behavior by no behavior.

Java

We can use the inheritance to make the method reimplementation in Java. They are similar with the ways we do in Smalltalk, like the overriding and overloading. But in Java, we can define abstract method and virtual method in Java, and it provides a @override tag that prevents the programmer from thinking another method is being overridden when really it is not.

Overridden method

If we want to override a method in subclass that inherited from the superclass, we should invoke the superclass method by using the keyword super. Below is the example:

public class Say {
	public void express() {
		System.out.println(“Hello wolrd!”);
}
}

public class Shout extends Say {
	@Override // @Override annotation in Java 5 is optional but helpful.
	public void express() {
		System.out.println(“Hello wolrd, loudly! ”);
}
}

Class Say represents the superclass and implements a method call express (). The subclass called Shout will inherit all the methods which in the Say class. But the class Shout will override the method express in Shout class, and replace it with new action.

Say hello_say = new Say();
hello_say.express();     // Prints “Hello wolrd!”

Say hello_shout = new Shout();   //Polymorphism
hello_shout.express();   //Prints “Hello wolrd, loudly!”

The super can be used to call the superclass's method; we can modify the class in class Shout to make the method express call another express method which in superclass Say.

public class Shout extends Say {
	@Override // @Override annotation in Java 5 is optional but helpful.
	public void express() {
		System.out.println(“Hello wolrd, loudly! ”);
		Super.express();  //will call the express method in class Say
}
}

But some of the methods cannot be overridden. For example, in Java, a method that is declared final in the super class cannot be overridden, and the method declared private or static cannot be overridden too. We cannot make a class that that is declared final to become a super class too.

Overloaded method

Java supports method overloading which allows the creation of several methods with the same method name, but different in their types of input and output of the function. We can define two methods with the same name in Java:

int Add(int a, int b) {	
	return a + b;}

float Add(float a, float b) {
	return a + b;}

The two methods have the same method name in define. To call the methods, we should pass the parameters to the methods above. When we call Add(1,1) it will call the first method Add(int a, int b), because we pass two int types of the parameters to the method Add. And if we call Add(1.0,1.0) it will call the second method Add(float a, float b) because we pass the float types of the parameters to the method Add. But if we give these two methods parameters a, b with default value, it will cause an error, which would result in an ambiguous call error, as the compiler wouldn’t know which of the two methods to use.

Abstract Method

Abstract method: When a class contains an abstract method, that class must be declared as abstract. The abstract method has no implementation and thus, classes that derive from that abstract class, must provide an implementation for this abstract method.

Abstract method are the method that with no body specification. Subclasses must provide the method statements to fulfill the abstract method. Especially, if the method was one provided by the superclass, we should override them in each subclass. If we forgot to override that will cause some problem like that the applied method statement may be inappropriate. Below is the abstract method example, we need to define the abstract method in subclass to reimplementation the method.

public abstract class Vehicle  // class is abstract
{
  private String name;
  public Vehicle (String nm)      // constructor method
  { name=nm; }
  public String getName()       // regular method
  { return (name); }
  public abstract void run(); // abstract method - note no {}
}

Virtual Method

Virtual method: A class can have a virtual method. The virtual method has an implementation. When you inherit from a class that has a virtual method, you can override the virtual method and provide additional logic, or replace the logic with your own implementation. A virtual method is a function or method whose behavior can be overridden within an inheriting class by a function with the same signature. In java, all the non-static methods with the default type “virtual function”. But if the methods marked with the keyword final, which cannot be overridden. The private methods are non-virtual which cannot be inherited. Below is the example for Virtual Method:

public class toy
{
   public void say()
   {
     System.out.println(“I am toy.”);
   }

   public static void main(String[] args)
   {
     List<toy> toys = new LinkedList<toy>();
	
     toys.add(new toy());
     toys.add(new toy_man());
     toys.add(new toy_car());

     for(toy current_toy : toys)
     {
	current_toy.say();
     }
   }
}

public class toy_man extends toy
{
   @Override
   public void say()
   {
      System.out.println(“I am toy_man.”);
   }
}

public class toy_car extends toy
{
   @Override
   public void say()
   {
      System.out.println(“I am toy_car.”);
   }
}

The output will be: I am toy. I am toy_man. I am toy_car.

Interfaces

In java interfaces are very similar with the abstract classes but all the methods in interfaces are abstract and all properties are static final. As the abstract classes, interfaces also can be inherited. And we also use the extends keywords too. Different from C++, in Java we cannot use the multiple inheritances for class, which means that a subclass extends from more than one superclass. An interface can tie elements of several classes together, and it also can separate design from coding as class method headers are specified but not their body. For example we build a running interface for the subclasses of Vehicle. Because there is a method called stop(), we should define the method in any class which using the running interface.

public interface running
{
  public void stop();
} 

When we create a class that will use the interface, we should use the keywords implements which will follow one or more interface list.

public class Vehicle_Run extends Vehicle implements running
{
    public Vehicle_Run (String nm)
    {
       super(nm);    // builds ala parent
    }
    public void run ()
    {
       System.out.println("I am running.");
    }
    public void stop ()  // this method specific to Vehicle_Run
   {
       System.out.println("I stop.");
   }
}

C++

C++ is very similar with Java in method overriding and method overloading. The method overloading of C++ we can refer the method overloading in Java. But they have difference in method overriding between C++ and Java.

Overridden method

C++ does not provide the keyword super that a subclass can use in Java to invoke a superclass version of a method that it wants to override. But in C++ we can use the base class follow with the scope resolution operator to override the superclass method. Below is the example:

class Toy {
public:
	Toy(char* nm, int s):name(name),size(s){}
	virtual void print() const;
private:
	char* name;
	int size;
};

void Toy::print() const{  // print() method of base class
	std::cout<<”Name = ” << this->name << “; Size = ” << this->size;
}

class Toy_car: public Toy {
public:
	Toy_car(char * nm, int s, int t) : Toy(nm,s), type(t) {}
	virtual void print() const;

private:
	int type;
};

void Toy_car::print() const{
	Toy::print();
	Std::cout<<”; Type = ” << this-> type;
}

The main method will call the print method in class Toy and class Toy_car separately.

Int main(int argc, char** argv) {
	Toy toy_test(“Jack”, 9);
        Toy_test.print();
        //outputs:
        //Name = Jack; Size = 9

        Toy_car toy_car_test(“Ann”,”7”,”1”);
        //the pointer to the most overridden method in the vtable in on Toy_car::print
        toy_car_test.print();// but this call does not illustrate overriding
        static_cast<Toy&>(toy_car_test).print(); // this one does
        //output
        // Name = Jack; Size = 9;Type = 1
}

Virtual Method

In C++ we declare the virtual method by add the virtual keyword before the function’s declaration. The concept of the virtual method is nearly the same with that in Java, but the function in C++ is not default by “virtual function”. Below is the example of virtual method in C++.

class Toy {
	public:
		virtual void say() const {
			std::cout << “I am Toy.” << std::endl;
}
};

class Toy_car : public Toy{
	public:
		void say() const {
			std::cout << “I am Toy_car.” << std::endl;
}
};

class Toy_man : public Toy{
	public:
		void say() const {
			std::cout << “I am Toy_man.” << std::endl;
}
};

int main() {
	std::vector<Toy*>  toys;
	toys.push_back(new Toy()) ;
        toys.push_back(new Toy_car()) ;
        toys.push_back(new Toy_man()) ;
	
        for(std::vector<Toy*>::const_iterator  it = toys.begin() ;  it != toys.end(); ++it){
	   (*it)->say();
	   delete *it;
        }

        return 0;
}

The output will be: I am Toy. I am Toy_car. I am Toy_man.

If we do not declare Toy::say() as virtual, the result will be: I am Toy. I am Toy. I am Toy.

Abstract Method

Quite similar with abstract method used in JAVA, the abstract method applied in C only generate a new virtual method without providing an implementation of that method. In fact, the derived classes have to overload the method and therefore to specify the implementation in that method. That's because abstract method provide no actual implementation in the body in which only exist a semicolon.


public abstract class Toy
{
   public abstract void action(speed s, direction d);
}
public class dog: Toy
{
   public override void action(speed s, direction d) {
      d.moveasDog(s);
   }
}
public class bird: Toy
{
   public override void action(speed s, direction d) {
      d.moveasBird(s);
   }
}

As we can see above, the Toy class defines a abstract method which allow dog and bird to move. Obviously the action method is abstract since no default implementation included. And in classes of dog and bird, we can see detail implementations in action method, so that specific action method has been defined according to individual behavior.

Ruby

Different with Java and C, Ruby is a dynamic Object-orientated Language. It allows default parameter and variable parameter used in the method. Therefore, there is no strict classification of overridden method and overloaded method.

Method with default parameter

With the code below, we can better understand this concept:

def sum( x, y, z=1 )
  x+y+z
end

puts sum(10,20,30)
puts sum(5.0,10)

Output: 60 16.0

def sum( *num )
  totalSum =0 
  num.each { |i| totalSum+=i }
  return totalSum
end

puts sum(1,10,100) 
puts sum(2.2,3.3,4.4) 
puts sum()

Output: 111 9.9 0

From the output we can see Ruby allow default parameter regardless the number and the type of parameter. Unlike the limitation in other static OO language, Ruby can dynamically execute codes in runtime. Therefore, the reimplementation in Ruby become less important compared with other static languages. Several ways can be used for method reimplementation in Ruby. For example, just simply extend classes. Or re-declare an existing class with new methods. To better demonstrate how these two approaches work, we use the code example as below:

Method extended from class

class Animal
attr_accessor:voice
  def initialize(v)
    puts "This is an animal."
    self.voice=v
  end
  
  def speak
    puts "This animal can speak"+self.voice
  end
end

class Dog<Animal
end

d=Dog.new(" bow-wow!!")
d.speak 

The output of this code is This is an animal. This animal can speak bow-wow!!

As we can see, we just simply create a descendant class Dog and use the default method in the ancestor class Animal. The dog can bark as “bow-wow”, so we use the voice parameter to make it particular for dog.

New method in the descendant class

Also, we can change the code of descendant class Dog since every dog can bark. Code shown below:

class Dog<Animal
  def initialize
    puts "This is a dog."
    self.voice=" bow-wow"
  end

d=Dog.new
d.speak

The output of this code is This is a dog. This animal can speak bow-wow

As we can see, the first line of the result is changed since we change the initialize in Dog class. And we do not need to define how the dog bark in the follow code since it has been defined in the Dog class. To further improve the code of this example, we can rewrite the Dog class again to make the speak method more specific. Codes shown below:

class Dog<Animal
  def initialize
    puts "This is a dog."
    self.voice=" bow-wow"
  end
  def speak
    puts "i am a dog and i can speak like this:"+self.voice
  end
end

The output of this code is This is a dog. i am a dog and i can speak like this: bow-wow As we can found in the result, the speak method is revised. This result is exclusive for describing dog.

If we just want to improve the functionality of the method in an ancestor class instead of rewriting it, we can use the keyword super in the code. Code for example is shown below:

class Dog<Animal
  def initialize
    puts "This is a dog."
    self.voice=" bow-wow"
  end
  def speak
    super
    puts "i am a dog and i can speak like this:"+self.voice
  end
end

The output of this code is This is a dog. This animal can speak bow-wow i am a dog and i can speak like this: bow-wow

From the result, we can see that both the speak methods in Animal class and Dog class are used.

Advantages and disadvantages of each languages applying method reimplementation

Smalltalk

Advantages: It is a very flexible program language. It allows any method to be overridden, so we can be very convenient to get the method we want by overridden. Smalltalk also support the single inheritance and single polymorphism. It helps us better understanding the structure of the inheritance of the objects, from the structure of the inheritance structure we can make the method reimplementation easily. For example in Strategy pattern, single inheritance of the objects will make it clear to display the inheritance hierarchy.

Disadvantages: Sometimes we do not require some methods in Smalltalk, but we still can override the methods which no longer need them. Single inheritance make the inheritance hierarchy clear, but sometimes multiple inerarchies could be a better choice for developing. It requires less codes to reimplementation the methods we want and provide a simple way to show the inheritance hierachy.

Java

Advantages: Java support the overriding methods, overloading methods, abstract methods, virtual methods, interfaces which make java be one of the top popular programming language today. We can reimplementation the methods by use the overriding and overloading methods. Abstract methods and virtual methods make the hierarchy structure colorful and clear. The interfaces of Java help to make some design patterns implementation easily. For example when we use the Strategy pattern, which clearly indicates a place in the design that will change, we should define the strategy as an interface.

Disadvantages: Java does not support the multiple inheritances. Compared with C++ which supports the multiple inheritances, C++ can provide more flexible way and more clear structure in class inheritance, which makes C++ sometimes a better choice for reimplementation methods in descendant classes.

C++

Advantages: C++ provide the multiple inheritance which makes C++ much more flexible in reimplementation the methods. With the multiple inheritances, it makes some of the design patterns work better. One benefit from it is Adapter pattern: it uses multiple inheritances to adapt one interface to another. This pattern usually used to provide implementation for an abstract class (when you want to use an existing class and its interface is not appropriate, so you need to glue another interface with that class).

The scope resolution operator in C++ is powerful enough. We can put the definition of the class in one file and use the scope resolution operator to implement the method in another file, which will make the whole structure more clear and it will help to add new method, overriding method, overloading method much easier.

With the keywords “virtual” we can make the virtual method in C++ much more flexible. From the above examples you can see that the “virtual” keywords it helps us easy to fulfill the polymorphism, makes the program much more efficiently.

Disadvantages: The multiple inheritances help us easy to reimplementation the method in subclass. But sometimes it causes some problems. With multiple inheritances, it makes the hierarchy of the program very complex. The programmers need to more time know the details of each base class. It will cause problem when using multiple inheritance is the diamond problem, which occurs when two classes are derived from a base class and another class is obtained by joining the derived classes together. If the derived classes override the same method from the base class when calling the method from the merged class and the joining class does also override that method, an ambiguity will rise. Another problem is that if two classes derived from the same base class. When we use these two classes to make multiple inheritance, it would make some base members duplicated.

Ruby

Advantages: Java and C++, both as static OO languages, use overload and overridden methods in polymorphism to extend the flexibility of programming, since the property and method of class cannot be changed during the runtime. Compared with Java and C++, Ruby, as a dynamic OO language, allows change of property and method in the class in the runtime. For this reason, the method reimplementation becomes less essential as that in Java and C++. Ruby does not require using complicated mechanism to define the methods for disambiguation. Thus, Ruby is considered has better performance in reducing code and providing concise program.

Disadvantages: The disadvantage of Rudy, to some extent, also results from its dynamic characteristic. It become unsafe and unreliable compared with static typing OO language. For example, method in Ruby does not care about the type of parameter. It may cause problem when the type of parameter is essential for some application. Ruby cannot identify this wrong type error made by programmer.

Conclusion

Reimplementation of methods is one of the most essential features in Object-Oriented language. It expands the flexibility of polymorphism in which descendant class can define their own behaviors and functions more efficiently. There are different ways for various languages to define how to reuse methods. In details, we use codes of Smalltalk, Java, C++ and Ruby to demonstrate and analysis how methods are reused, following with the discussion of advantages and disadvantages of method reuse in these four languages.

References