CSC/ECE 517 Fall 2012/ch1b 1w46 sm
Subclassing
Subclassing
Subclassing is a principle of creating a specialization(subclass/ [1]) of a base class(superclass/ parent class) by inheriting the methods and instance data from the base class. Subclassing is one of the significant features of OO programming, which greatly increases the reusability of classes and also minimizes duplication of code. A subclass usually inherits some properties from a super class. Inheritance is a design principle in object oriented languages like Java. The reason behind inheritance is to reuse the code of existing objects or establish a subtype from an object. This greatly improves the efficiency and makes the code more readable as methods which are written only once can be used by a lot of subclasses. A superclass consists of common features that can be extended over subclasses. Superclass and subclass are commonly called base and derived class. The relationship between a subclass and superclass can be represented by a simple UML diagram as shown below.
Examples of Subclassing
The following example illustrates method overriding in Java.
public class Base{ public String printName(){ System.out.println("Base Class"); } } public class Child extends Base{ public String printName(){ System.out.println("Child Class"); } } public class Test{ public static void main(String args[]){ Base b = new Base(); b.printName(); //prints "Base class" Child c = new Child(); c. printName(); //prints "Child class" b = c; b.printName(); //prints "Child class" } }
The following example illustrates subclassing in C++
#include <iostream.h> class Base { public : virtual void printName() { cout << "Base Class"; } }; class Child:public Base { public : void printName() { cout << "Child Class"; } }; void main() { Base b; Child c; Base *b2 = new Child(); b.printName(); c.printName(); b2->printName(); }
Advantages of Subclassing
Reusability
By creating subclass, the code of the base class can be reused in many situations. This gives subclass the freedom to create more specialized functions. It can use the base class methods to create these special functions. Apart from avoiding code duplication, this helps in decreasing the file size thereby saving some memory space.
For instance, we maybe programming animal characteristics but there is a special class of animals called mammals. Mammals display some specialized functions which can be defined in the subclass. As per Liskov Substitution principle which is discussed later, mammals obey the constraints enforced by the animal class and hence this kind of implementation avoids code duplication.
Polymorphism
This allows multiple definitions for the same method depending on the object it is invoked on. Subclasses of a class can define their own unique behaviors and yet share some of the same functionality of the parent class. For example,
public class Car{ public String getEngineType(){ return "Basic" } } public class Mercedes extends Car{ public String getEngineType(){ return "Advanced" } } public class BMW extends Car{ public String getEngineType(){ return "Moderate" } } public class Tester{ public static void main(String[] args){ Car c = new Car(); c.getEngineType();// returns "Basic" c = new Mercedes(); c.getEngineType();// returns "Advanced" c = new BMW(); c.getEngineType();// returns "Moderate" } }
In the above example, even though we have used the same variable type, the actual method invocation at runtime invokes the appropriate object's method and not the method defined by the variables's type.
Disadvantages of Subclassing
Tight Coupling
Inheritance causes tight coupling between base class and subclasses. We cannot freely modify one without modifying (or at least performing substantial testing to make sure it doesn't break existing functionality in the other) the other.
The fragile base class problem: Base classes are considered to be fragile because we cannot make changes to it and be sure that it doesn't impact any of the sub-classes without testing all of them. Moreover, we also need to test the client code which makes use of the base class and sub-class objects to make sure there are no regression bugs. A simple change to a base class can leave the system inoperable.
Security problems: If a new feature is added to the base class which is required by a subset of sub-classes, then by adding it, we might accidentally provide access to the other sub-classes which shouldn't have access to it. This problem might not be realized until a security breach has occurred and it can have disastrous consequences. Also, there is no easy way to fix this with sub-classing and an alternate method needs to be looked for providing this feature to the sub-classes, possibly, by implementing a particular interface.
Using subclassing solely for code reuse
If a class inherits another class solely for code reuse and it doesn't have a IS-A relationship with this parent, this can lead to bloated and quite possibly buggy code. It can lead to bloated code if the subclass only makes use of one method in the parent(a large class with numerous methods). It can lead to buggy code if the base class decides to change the signature or possibly delete the methods which it knows for sure that the subclasses won't need. This will cause problems for those subclasses which do not exhibit the IS-A relationship and are improperly added to the class hierarchy. This defeats the whole purpose of inheritance and thinking in terms of objects and hierarchies.
Multiple Inheritance Problem
When a subclass inherits from more than one parent and both parents have the same method signature, then a conflict arises when we invoke the method on the subclass object.
#include <iostream.h> class Base1 { public : void printName() { cout << "Base1 Class"; } }; class Base2 { public : void printName() { cout << "Base2 Class"; } }; class Child:public Base1, Base2 { }; void main() { Child *c = new Child(); c->printName(); }
In the above example, the method PrintName is invoked on a child object. But, a conflict arises as to which method of the two parents needs to be invoked
Conclusion
Subclassing provides an elegant way to capture the hierarchical relationship among classes and promotes code reuse by defining common behavior in the base class and only providing the variations in the subclasses. Code reuse should not be the only reason for subclassing and the subclass should satisfy IS-A relationship.
References