Tag: OOP

Object-oriented programming (OOP) is a programming paradigm which uses objects to design applications. OOP has 3 big principles: Encapsulation, Inheritance, Polymorphism. When correctly used, these principles plus Overriding/Overloading can help developers to design flexible, extensible, and maintainable applications. When not used correctly, maintainability becomes expensive and flexibility compromised.

Nowadays, OPP seems to be a basic subject, something everyone should be comfortable with. However, having worked in many projects until now, I think this is not completely true. There are uncountable applications making poor usage of OOP concepts.

This article brings a quick review of OOP principles and some best practices. The goal here is not to detail every aspect of OOP, but to present the main principles, advantages of good usage and some examples.

Code samples in this post were compiled with a JDK 6.

Encapsulation

Encapsulation consists in not exposing implementation details of a class. It helps programmers to make changes without breaking other parts of the code. But how to do that?

In the previous example, the instance variable attribute from class NotEncapsulated is visible for everyone, since it is declared public. There are many drawbacks when designing like that. First of all, validation becomes difficult to implement when exposing a public variable. So other classes can assign any value to that variable, even a value that might be unexpected. Secondly, cohesion and loose coupling are not respected, since other components will know details from this object.

Next, we are going to refactor the above example to apply the encapsulation principles.

Inheritance

Inheritance is the mechanism by which a Java object can specialize another Java object members (instance variables and methods). In Java, we can create inheritance relationships by extending a class (using keyword extends).

The common reasons to use inheritance are:

to take advantage of polymorphism

to be able to reuse code

Reusing code is an essential aspect of OOP. Objects can inherit members of less-specialized objects avoiding code redundancy. Here is an example:

Polymorphism (which means many forms) allows specialized classes to be accessed without being necessarily known at compile time. When used correctly, the code becomes clear and maintenance becomes easier. We will talk about polymorphism later on this post.

Note: In order to avoid the “Deadly Diamond of Death” problem, Java does not support multiple inheritance. Each Java class can use the keyword extends over only one other class.

IS-A and HAS-A relationships

IS-A is an object relationship based on inheritance and implementation of interfaces. If we have a class Plane and a class Boeing which inherits from Plane, we can say that Boeing IS-A Plane. In Java, we express IS-A relationships using the keywords implements to implement an interface and extends inherit from a class.

A HAS-A relationship is characterized when an object uses another object as a member. Imagine that a class Computer has, as an instance variable, an object called Processor. So we know that Computer HAS-A Processor. HAS-A relationships are also known as composition.

Even if these two relationships have each one their advantages and drawbacks, whenever you face a situation where you need to chose one of them, favour composition over inheritance. Composition tends to make the design more flexible and easily testable, compared to inheritance.

Polymorphism

In Java, Polymorphism is the ability Java objects have to assume many forms. Any Java object that pass more than one IS-A test can be considered polymorphic. As all Java objects inherit from class Object, they are all polymorphic since they pass the IS-A test for class Object and for their own type. Logically, this rule is not applicable for class Object itself.

As shown above, the classes Gull and Chicken inherit from Bird class. Therefore, we used an interface called Flyer to give some birds the ability to fly. Thanks to instanceof operator we can identify during runtime which birds can fly. In this example, the object Gull takes the form of the Flyer interface.

Overloading and Overriding

An overloaded method is a method that reuses the name of another existing one with different arguments and/or return type. Overloading a method is generally used when developers need to redefine the signature of an existing method.

On the other hand, overriding occurs when we have in a subclass a method with the same signature of a method inherited from a superclass (unless the superclass method is marked final). The main advantage in overriding is that we can redefine a behaviour of a particular subclass.

When overriding methods some rules apply:

Methods marked final or static cannot be overridden

The parameter list must be the same compared to the overridden method (if it is not the case overloading will occur)

The return type must be of the same type or a subtype of the return type of the original method

The overriding method cannot be more restrictive in its access modifiers than the method being overridden (if a method have public access the overriding method cannot be private)

A method can be overridden only if it is inherited from the super class (private and final methods are not overridden)

Overriding methods can throw unchecked (runtime) exceptions

The overriding method cannot throw checked exceptions that aren’t the same or a subtype of the exception of the overridden method

You must override abstract methods from abstract classes, unless the subclass is also abstract

Since Java 5, we have the @Override annotation to help us to override and not overload a method. If a method is annotated with the keyword @Override and it does not override any method, compilers will inform you ;