CSC/ECE 517 Fall 2011/ch6 6b mm

From Expertiza_Wiki
Jump to navigation Jump to search

Subclassing

Introduction

Subclassing, or inheritance, in object-oriented programming grants the programmer the ability to extend and refine the functionality of existing classes. This principle has many advantages that can make the programmers life much more simple. Principles and techniques have been defined that support and oppose the use of subclassing, making for a great debate. Here, some of these principles will be discussed to help determine when it is a good idea to utilize subclassing in a program.

Reasons For Subclassing

There are many different reasons out there for why subclassing is helpful:

  • Specialization - If an existing class has the basic functionality needed (ex: a circle class) but is missing some small details (say a method to determine the center of the circle), then subclassing is very useful. It allows the programmer to get most of the functionality needed and provides a means to specialize the superclass to fit the needs of the program.<ref>Skrien 2009</ref> An good example of this is the inheritance that takes place within Java's AWT graphical components<ref>Budd 2004</ref>. Each of the components you see within a GUI (Label, Button, Checkbox, etc.) are specialized versions of the Component class.
  • Generalization - Two classes that are very closely related to each other could have their common functionality pulled out into a superclass and then changed to implement that superclass. This provides the programmer with less code duplication and makes it easier if a change is needed to that common functionality later on<ref>Skrien 2009</ref>. An example of this found on SourceMaking.com is the extraction of common information between a piece of luggage and a piece of cargo (identification, weight, ID number) into a superclass called freight and having the luggage and cargo classes extend the freight class<ref>SourceMaking.com</ref>. This produces the generalized freight class and lets the luggage and cargo classes become specialized.
  • Polymorphism - Polymorphism is basically the utilization of a superclass type for a variable to hold a reference to an object whose class can be the supertype or any of its subtypes.<ref>Venners 1998</ref> This is a great advantage since storing a subtype into that variable would still allow the program to function correctly. In the below example, a variable that is typed as a Fruit could store the information for a Fruit, Apple or a Banana.
class Fruit {
  // . . . . 
}

class Apple extends Fruit {
  // . . .
}

class Banana extends Fruit {
  // . . .
}

var my_fruit:Fruit = new Apple();
 . . . 

// Oh wait, I wanted a banana instead...
my_fruit = new Banana();
  • Dynamic Binding - This feature of subclassing allows the code executor (ex: JVM for Java) to determine at runtime which version of an overridden method to execute. This is needed because sometimes the method to execute cannot be determined at compile-time.
  • Code Reuse - When a superclass is extended, the subclass inherits all of the methods and properties of the superclass. Code duplication is held to a minimum since the code is reused so effectively.
  • Overriding and Overloading - Overriding and overloading functionality in a subclass is a major draw of subclassing. These abilities allow the programmer to tweak specific methods of the superclass that are close to the desired functionality in order to get the functionality exactly right.

Reasons Not To Subclass

  • Inherits Too Much - Sometimes a programmer only needs a subset of the functionality of class. Subclassing requires that all functionality be carried down to the subclass though, often resulting in a class that has a lot of unneeded functionality. If this is done more than once in a hierarchy, then subclasses can become too bulky very quickly.
  • Superclass Fragility - This issue arises because sometimes a small change in a superclass can sometimes have a ripple effect that forces changes to be made elsewhere in the code. This is usually due to poorly designed superclasses where there is not a clean separation between interface and implementation.<ref>Venners 1998</ref>
  • Weak Encapsulation - This idea crops up when a subclass utilizes functionality of the superclass as it is inherited, essentially creating a case where the code from the superclass is reused. However, if changes are made to that functionality in the superclass, it could break code that directly utilizes an instance of the subclass.<ref>Venners 1998</ref>
  • Forces Is-A Relationships - There is a time and a place for is-a relationships, but there are some in the field that believe "almost every is-a relationship would be better off re-articulated as a has-a relationship"<ref>Sumption 2011</ref>. Instances where two subclass functionalities need to be combined make for a hard implementation using is-a relationships. As an example, lets take the classes that follow. If we came across an instance where a TA was also a student, it would be very hard to pull in the functionality of one into the other. Composition (using has-a relationships) is a better fit here so that a Person could have the functionality of a Student, a TA, or both.
class Person {
  var name:String;
  
  function sayHello() { . . . }
}

class Student extends Person {
  var student_id:int;

  function payTuition() { . . . }
}

class TA extends Person {
  var class:String;

  function gradeTest() { . . . }
}

Subclassing Principles

Liskov Substitution Principle

Probably the most well-known subclassing principle is the Liskov Substitution Principle (LSP). It states:

If S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may be substitutes for objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.).<ref>Wikipedia - LSP</ref>

In other words, superclasses should be replaceable by their subclasses without altering the system in any way. This principle borrows ideas from the design by contract methodology by imposing restrictions on the conditions for which the methods of a class will function. It also restricts how subclass methods can be designed in terms of their return types (covariance) and method arguments (contravariance). These restrictions provide a very good ruleset to determine whether subclassing should be utilized or not.

Design by Contract

The Design by Contract principle has a direct application to subclassing in its definitions of pre- and postconditions that must exist on methods. Its main goal is to provide a method to define "formal, precise and verifiable interface specifications for software components" by utilizing the idea of preconditions, postconditions and invariants<ref>Wikipedia - DbC</ref>. When used, it helps to guarantee the functionality of any software component.

Following this principle allows a programmer to weaken preconditions and strengthen postconditions and invariants, but not vice versa. Utilizing these rules comes very close to mimicking the rules applied in the Liskov substitution principle.

An example of how Design by Contract is implemented can be seen in a method that raises a number to a power (ex: 2^4 = 16)...

// @param base - float value to be exponentiated
// @param exponent - float number of times to exponentiate
// @returns float - value of exponentiation
function exponentiate (float base, float exponent) {
  // . . .
}

From this definition, the preconditions are defined by the arguments that are required by the method (base and exponent). The comments and the typing of the variables requires the values passed in to be floating-point numbers. It is the clients responsibility to ensure that it passes in floats and not a string or an array. The postconditions are defined by the return value for the method. The comments state that it will return an floating-point value representing the end result of the exponentiation.

References

Citation Notes

<references/>

Full References

"Generalization, Specialization, and Inheritance" http://sourcemaking.com/uml/modeling-it-systems/structural-view/generalization-specialization-and-inheritance

Budd, Timothy. "Introduction to Object Oriented Programming, 3rd Ed Slides". http://classes.engr.oregonstate.edu/eecs/winter2004/cs582/slides/chap08/slide16.htm. 2004

Sumption, Bernie. "Inheritance is evil, and must be destroyed". http://berniesumption.com/software/inheritance-is-evil-and-must-be-destroyed/. 2011

Skrien, Dale. "Object-Oriented Design Using Java". Boston: McGraw-Hill, 2009.

Venners, Bill. "Inheritance versus composition: Which one should you choose?" http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html. 1998

Wikipedia. "Design by Contract". http://en.wikipedia.org/wiki/Design_by_Contract

Wikipedia. "Liskov substitution principle". http://en.wikipedia.org/wiki/Liskov_substitution_principle

External Links

http://en.wikipedia.org/wiki/Liskov_substitution_principle