Object Oriented Programming

"I object to objects being treated so objectively." – Anonymous

Main OO Topics


Abstraction

Abstraction is an important concept in object-oriented programming. One of the challenges of developing software is representing the most appropriate and optimal solution to a problem. Some solutions may be too general and others may be too detailed. How much functionality (or detail) should a system, subsystem or even on object have? The solution should optimally cater to the current problem (or requirements) and be flexible enough to easily handle change. Due to overwhelming software complexity, object-oriented programming has fundamental units called classes that can encapsulate functionality or abstract another client class from the internal details of a server class. Thus a client class would only have to deal with the interface of the server class and not be overwhelmed by its internal structure.

A good analogy of abstraction is how we use our cars. Unless you're an auto-mechanic or equivalent, you do not have to be concerned with the exact internal workings of your car's engine when you drive it. What you are concerned with is interacting with the car via its interface such as the gas petal, the brake, the steering wheel, the dashboard, etc. Furthermore, a car's engine has improved and changed throughout the decades but its basic interface has not changed. As a driver you would do the same thing as a driver would do twenty years ago, that is step on the gas petal to move, step on the brake to stop, turn the steering wheel to turn, etc. Hence, your knowledge of the car can be considered abstract.

Another analogy is looking at a map for locating streets and towns. If I wanted to know the exact street address of a destination I would zoom in to that level of abstraction. If I wanted to know the location of the nearest major city from a given location I would zoom out to that level of abstraction, thus not concerned with the exact locations of every single back road in between. So, the main point of the map analogy is to use the level of detail that you feel will satisfy your request and ultimately meet your needs (at the system, subsystem and class levels).


Class vs. Type

The class for an object defines how that object is implemented by defining its operations and internal state. By contrast, the type of an object is its interface. However, their relationship is that classes define an object's type. So when an object supports the interface defined by a class, it is customarily said that an object is an instance of that class.

This also brings about the notion of class inheritance and interface inheritance or subtyping. For interface inheritance, an object can be used in place of another object. By contrast, class inheritance describes an object's implementation in terms of another. Keep in mind, there are three types of inheritance: 1) Extension, 2) Specialization and 3) Restriction. Extension is sub-typing and specialization and restriction is sub-classing. Furthermore, remember to "program to an interface, not an implementation", which is also a common theme for design patterns.


Black-box Reuse

Black-box reuse is when one class uses another without knowing the other's internal contents. There are five types of black-box reuse including genericity. Black-box reuse can have a variety of multiplicity's and directions, unidirectional or bi-directional one-to-one, one-to-many or many-to-many. In the case of the many, some sort of collection class would more than likely be used. For instance, a client class could have an array or vector type for the many server objects (of the same type). In the bi-directional case, each object could pass a reference to the other object but beware of infinite recursion, in this case.

Dependency

Dependency is the weakest type of black-box reuse. Dependency is when one object "uses" another object in such a sense that whatever changes are made to the client object the server object changes accordingly, hence the server object is dependent upon the client object. A good example is a TV object, which depends on the remote control object. In a UML class diagram a dependency is represented by a dotted or dashed line with an arrow pointing to the server object. In OO code, a dependency can be represented by passing a (value) parameter server object to a method of the client class.

Association

Association is when a client object knows about or has a significant/interesting relationship with one or more server objects, in such a fashion that the client object can be created without the creation of the server object(s). Within a UML Class Diagram, a solid line (with an optional arrow pointing to the server object) represents an association. An example of an association could be a textbook's association to a college course. In OO code, an association can be represented by passing a (value) parameter server object to a method of the client class.

Aggregation

Aggregation is a whole/part association where a client object contains one or more server objects but the client object can be defined with or without the creation of the server object. For instance, a car can contain golf clubs in its back trunk but the car is still a car with or without the golf clubs. Within a UML Class Diagram a solid line between classes having an empty diamond at the client or aggregating object's end represents aggregation. In OO code, an aggregation can be represented by an attachment via a (reference) parameter.

Composition

Composition is a stronger whole/part association where a client object is composed of one or more server objects, such that without the creation of the one or more server objects the client object could not be created. For instance, a car's composition consists of an engine among other objects. However, the car would not be considered a car if it did not have an engine. Within a UML Class Diagram a solid line with a filled diamond at the client or composing object's end represents composition. In OO code, instantiation of the server object takes place within the client object (before the client is fully instantiated).

As an example of black-box reuse view: http://www.bletchleypark.net/algorithms/Blackbox.pdf


Aggregation vs. Inheritance

Another comparison that I want to elaborate on is aggregation (black-box reuse) vs. inheritance (white-box reuse). Which reuse mechanism is better? Well, object aggregation is defined dynamically or at run-time via object references. Since only interfaces are used, the advantage of aggregation is the integrity of encapsulation. The implementation of the routines of an object are another object's interfaces, which implies low implementation dependencies. An extreme case of aggregation is delegation, which is where a receiving object delegates operations to its delegate object. The significant disadvantage of aggregation is the increase in the number of objects and their relationships.

On the other hand, inheritance is defined statically or at compile-time. Inheritance allows for an easy way to modify implementation for reusability. Of course, the significant disadvantage of inheritance is that it breaks encapsulation, which implies implementation dependence. So when a redesign needs to occur or a domain change is required, the parent class must be replaced which more than likely implies a whole entire inheritance lattice rework or at the very least the parent class needs to be rewritten which also affects sub classes as well. So the tendency is to "favor object aggregation over class inheritance."


Unified Modeling Language

The Unified Modeling Language, UML, is a set of well-defined semantics for representing many aspects of an OO software system. It is used to model systems from conception to implementation. There are ten fundamental diagrams of UML:

  1. Use Cases Diagram
  2. Class Diagram
  3. Sequence Diagram
  4. State Chart Diagram
  5. Activity Diagram
  6. Package Diagram
  7. Object Diagram
  8. Collaboration Diagram
  9. Component Diagram
  10. Deployment Diagram

Genericity

Genericity, a static technique, further extends the concept of reusability in OOP. A generic abstract data type can capture and encapsulate the same operations for many types. Generics are also referred to as parameterized types, essentially meaning that a type is used as the argument of the generic class and not an object of a type. Genericity comes in two flavors: unconstrained where no requirements exist for the parameter and constrained where the parameter has special operations. C++ provides genericity via templates not only at the class level but at the function level as well.

Collections are probably the most common generics used. For instance a stack data structure can capture a LIFO (last in first out) relationship for another type. Why spend the time building many stacks for each unique type when a template or generic class can represent the operations for all of those unique types. Let's say that if a stack is developed uniquely for each case then it would be a mess if there needed to be an implementation change for each stack, where as with genericity the change can be made once and then all surrounding code would not have to go through any changes. So the algorithm that would work for a double would work for an int or any other type. Essentially a generic type represents an abstract relationship for another type. For example, take a pair. Two types can have an association relationship when using a generic class such as a pair.

As an example view: http://www.bletchleypark.net/algorithms/Genericity.pdf


Polymorphism

Polymorphism literally means many forms. In OOP polymorphism refers to a variable type that can reference another object of different type during run time. Polymorphism can exist for parameter passing as well as via assignment. Upon creation, objects always maintain their type but that object can change what it refers to. Remember, p = c and not vice versa.

Another form of Polymorphism exists when a routine (signature) can refer to different implementations from the specified inheritance lattice. Essentially, polymorphism can mean overloading or overriding. An overloaded routine is one that has the same name of another routine but with a different formal argument list. An overridden routine is a method that has the same name, return type and the same formal argument list.

As an example view: http://www.bletchleypark.net/algorithms/Polymorphism.pdf


 

Multiple Inheritance

Multiple inheritance refers to a class obtaining properties from 2 or more classes. The biggest issue with multiple inheritance is routine name clashing. For example when one inherited class has a routine name called density and another inherited class has the same routine name, then which one is to be used? Under this situation a compile time error occurs. Name clashes are resolved by renaming at least one of the inherited routines or by using some kind of scope resolution operator if your language of choice allows such adjustments.


Repeated Inheritance

Repeated inheritance is a class inheriting features from another class via two or more different classes. Routines in the grandfather class are treated as one in the child class. However, if a routine is redefined within one or more parent classes (inheriting from the grand father) then the single child class will have to rename at least one of them - provided renaming is allowed.

However, a dynamic binding problem could exist. Let's say the child identifier is assigned to a grandfather identifier. Let's also say that the routine above is called density. When the grandfather identifier invokes perimeter which parent density routine is used? They're both renamed in the child class but density is not renamed in both parent classes that inherit that routine from the grandfather class.


Dynamic Binding

Dynamic Binding or late binding refers to determining what implementation a routine uses during the execution of an OO application. Usually, late binding takes a small performance hit. So the compiler usually tries to bind as much as it can statically whenever it is clear on which version of a method to use. Statically, code can be written so that a type has the opportunity to change during run time. However, during compilation the compiler has the possibility of not knowing what implementation to use so it defers that binding to run time. One clear advantage is from the following scenario. Let's say that in a system there exist twenty related types each having the same function signature but implemented in its own way. Because these types aren't bounded by an inheritance hierarchy, in order to find out which function to use one would have to have twenty if statements. In the more efficient case, with dynamic binding you only need to invoke the routine once and at run time the correct implementation will be invoked.

Note when a parent references a child object and a routine is invoked, that routine better at the very minimum be in the parent class. If there is more than one (of the same) routine then the most appropriate routine will be invoked. So for instance, if a parent type is pointing to a child object and they both have the same routine and a call to that routine is made against the parent type then the child routine will be invoked.

Note also that an abstract or deferred type can be declared but not instantiated. Dynamic binding can reduce the amount of code needed to invoke a simple routine. During design time inheritance can capture common characteristics of many objects such that common routines can be invoked without explicitly calling each specific type's routine. For instance in traversing an array that holds parent types, the elements can be of descendant type and the parent routine could be called without having to worry about which implementation is being used - hence it happens at run time.

As an example view: http://www.bletchleypark.net/algorithms/DynamicBinding.pdf


Static Typing

The actual code is subjected to a set of rules during compile time. This is to ensure that no type violations occur during run time such as invoking an invalid routine or its parameters are not acceptable by disallowing data type conversions that would cause data loss. Typing is different than binding because typing essentially ensures that at least one operation exists where as binding is concerned with choosing the right operation. Statically typed languages such as C++, provide mechanisms such as inheritance to determine the assignment type. So, if a variable is declared as an apple then only apples can be stored in that variable. A more general type can be associated with a more specific type but not vice versa. Static typing brings about reliability in that errors are caught early, readability where every entity and routine is declared with a certain type and efficiency such that an object is bounded by its type and nothing else. Eiffel, C++ and Java are some languages that support static typing.


Static Binding

Static binding or early binding refers to binding all routine calls to some class during compile time. If the compiler cannot determine the type then a compilation error will occur. This actually lends itself to greater optimization in that the overhead is less compared to having to bind calls during run time. However, this is only the case if an object is assigned to only one identifier where that identifier is not declared anywhere else and the routine called on it is not polymorphic. C++ supports static binding by default. Using the keyword virtual permits dynamic binding in C++.


Dynamic Typing

The verification of type occurs during run time. Types in a dynamically typed language would not have to be determined during compile time and therefore are deferred to run time. However, this could cause typing violations during run time, unfortunately when it would be too late. Smalltalk uses dynamic typing, which implies waiting to run time to determine if there is an applicable routine.


Contract Programming

Programming by contract is becoming more and more prevalent in object-oriented programming. Objects communicate with each other in a client-supplier relationship. In order to validate this relationship the client must promise the supplier what he needs so that the supplier can perform properly and in return the supplier must promise the client what it needs. So a contract is set forth for the client and supplier to agree upon via a specification.

Contract programming is the application of assertions. Assertions come in three forms: (1) Pre-Condition, a (2) Post-Condition or an (3) Invariant. Pre- and Post-Conditions are contained within routines. An Invariant is a condition that has to hold true throughout the entire life of the object regardless of anything else. Ideally clients abide by supplier routine pre-conditions and suppliers abide by its routine post-conditions.


Covariance

A covariant typing strategy allows functional redefinition so that the return type is a descendant of the original functional return type. Also, parameters can take on this same typing strategy when redefining routines. A parent can always be a child but a child can never be a parent.

However, type safety could lead to problems. Let's say that a child class a routine is redefined from a parent class. When that child type is assigned to that parent type and that routine is called on the parent identifier the child's routine will truly be invoked. Let's say that via covariance that redefined child routine's parameter(s) are declared as descendants of it's parent routine's parameter(s). What if a parent type parameter is used for this routine? It can still compile but could be a defect at run time because that redefined routine in the child, expecting a child type parameter gets a parent type parameter, could use a property bound to only that child type and not to it's parent (parameter's) type.

If the child's routine parameter(s) is more general than the parent's routine parameter(s) then by contravariance type safety is achieved. Nevertheless, ultimately the parameter(s)'s type should be the same for that required of the routine to avoid covariant problems.

Links

 

http://java.sun.com/docs/books/tutorial/java/concepts/

http://www.zib.de/Visual/people/mueller/Course/Tutorial/tutorial.html

http://www.objectfaq.com/oofaq2/

http://www.adtmag.com/joop/index.asp

http://bepp.8m.com/english/oop/

http://www.rescomp.berkeley.edu/~hossman/cs263/paper.html

http://liinwww.ira.uka.de/bibliography/Object/

http://csis.pace.edu/~bergin/patterns/ppoop.html

http://directory.google.com/Top/Computers/Software/Object-Oriented/