3.1. Object-Oriented Programming Concepts

The two primary characteristics of object-oriented programming are inheritance and polymorphism:

  1. Inheritance allows us to group classes into families of related types, allowing for the sharing of common operations and data. For example, think of the family of exception classes that we looked at in Section 1.17.

  2. Polymorphism allows us to program these families as a unit rather than as individual classes, giving us greater flexibility in adding or removing any particular class.

Inheritance defines a parent/child relationship. The parent defines the public interface and private implementation that are common to all its children. Each child adds to or overrides what it inherits to implement its own unique behavior.

An AudioBook child class, for example, in addition to the title and author it inherits from its parent Book class, introduces support for a speaker and a count of the number of cassettes or CDs it represents. In addition, it overrides the inherited check_out() member function of its parent.

In C#, the parent is called the base class and the child is called the derived class. The relationship between the parent or base class and its children is called an inheritance hierarchy. At a design review meeting, for example, we might say, “We intend to implement an AudioBook derived class. It will override the check_out() method of its Book base class. However, it will reuse the inherited Book class data members, properties, and member functions to manage its shelf location, author's name, and title.”

Figure 3.1 pictures a portion of a possible library lending-material class hierarchy. The root of the class hierarchy is an abstract base class, LibMat. LibMat defines all the operations that are common to all the different types of library lending materials: check_in(), check_out(), shelf_location(), fine(), due_date(), and so on. LibMat does not represent an actual lending-material object; rather, it is an artifact of our design. In fact, it is the key artifact. We call it an abstract base class.

Figure 3.1. Portion of the Library Lending-Material Class Hierarchy


In an object-oriented program we indirectly manipulate the class objects of our application through an abstract base class rather than directly manipulating the derived-class objects of our application. Indirect manipulation allows us to add or remove a derived class without having to modify the existing program. For example, here is a possible implementation of a loan_check_in() member function of our Library class:

class Library
{
   public void loan_check_in( LibMat mat )
   {
          // mat refers to a derived-class object,
          // such as Book, RentalBook, Magazines, etc. ...
          mat.check_in();

          if ( mat.is_late() )
                 mat.assess_fine();

          if ( mat.waiting_list() )
                 mat.notify_available();
   }

   // ... rest of the Library class definition
}

There are no concrete LibMat objects in our application—only Book, RentalBook, and AudioCD objects, as well as actual library materials that users borrow. How does this function work? What happens, for example, when the check_in() operation is invoked through mat? For this function to make sense, mat must somehow refer to one of the actual class objects of our application each time loan_check_in() is executed. In addition, the check_in() member function that is invoked must somehow resolve to the check_in() instance of the actual class object to which mat refers. And this is what happens. The question is, How does it work?

The second unique aspect of object-oriented programming that we identified earlier in this section is polymorphism, the ability of a base-class object to refer transparently to any of its derived-class objects. In our loan_check_in() function, for example, mat always addresses an object of one of the classes derived from LibMat. Which one? That cannot be determined until execution of the program, and it is likely to vary with each invocation of loan_check_in().

mat.check_in();

the instance of check-in() to be executed is determined at compile time according to mat's class type. Because the function to invoke is resolved before the program begins running, this is called static binding.

In object-oriented programming the compiler cannot know which instance of check_in() to invoke, and so the resolution is delayed until runtime. It is based on the actual derived-class object to which mat refers to with each invocation of loan_check_in(). This is what we mean by dynamic binding. It is a fundamental aspect of object-oriented programming.

Inheritance allows us to define families of classes that share a common interface, such as our library lending materials. Polymorphism allows us to manipulate objects of these classes in a type-independent manner. We program the common interface through an instance of an abstract base class. The operation is not determined until runtime, according to the type of the object addressed.

If the library decides no longer to lend interactive books, we simply remove the InteractiveBook class from the inheritance hierarchy. The implementation of loan_check_in() does not require any changes. Similarly, if the library decides to charge a rental fee for certain audio books, we simply implement a derived AudioRentalBook class. loan_check_in() still does not require changes. If the library decides to lend laptop computers or video game equipment and cartridges, our inheritance hierarchy can accommodate each of those options as well.[1]

[1] Note that struct declarations are not part of object-oriented programming. It cannot be the object of inheritance, nor can it participate in dynamic binding.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.136.17.12