© Vaskaran Sarcar 2020
V. SarcarInteractive Object-Oriented Programming in Javahttps://doi.org/10.1007/978-1-4842-5404-2_9

9. Quick Recap of OOP Principles

Vaskaran Sarcar1 
(1)
Bangalore, Karnataka, India
 
Welcome to the final chapter of Part I. So far, you have learned the fundamentals of object-oriented programming with the basic building blocks in Java. Before you proceed to Part II, let’s review the core principles that you have already covered in this book.
  • Class and objects . Throughout the book, in almost every example, I have used different types of classes and objects. The use of the static keyword was little bit different, and you accessed the static fields through the class names.

  • Polymorphism . Both types of polymorphism were covered. Compile-time polymorphism was covered through method overloading, and runtime polymorphism was covered through method overriding techniques. You have seen that dynamic method dispatch is an important concept in Java.

  • Abstraction. This feature was tested through abstract classes and interfaces.

  • Encapsulation . Each class with different access modifiers can be considered in this category. But a better example can be a class with a private member and a getter-setter. As per experts’ suggestions, you should make your instance variables private and access them through public getter-setter methods.

  • Inheritance . You explored different types of inheritance in multiple chapters.

  • Message passing. Normally, a message for an object is to request to invoke a method in the receiving object. In simple words, message passing is just communication among different objects. This feature is very common in a multi-threaded environment. But you experimented with runtime polymorphisms, in which a super class reference pointed to a subclass object, and this can also be considered in this category. Multi-threading is discussed in Chapter 11 in the book.

  • Dynamic binding . Runtime polymorphism through method overriding examples can fall into this category.

Q&A Session

9.1 Can you summarize the difference between abstraction and encapsulation?

The process of wrapping up the data and methods into a single entity is known as encapsulation. Using this technique, you can prevent arbitrary and unsecured access to your data. You can use different access modifiers to restrict direct access to your data. But the use of getter and setter methods is a better example in this category. In case of encapsulation, your entire code works like a capsule, so it is termed as an encapsulation.

In abstraction, you show the essential features but hide the detailed implementation (or background details) from the user; for example, when you use a remote control to switch on a television, the internal circuits of the device are not your concern. You are OK with the device as long as your preferred channel appears properly on your television once the button is pressed.

Note

Encapsulation focuses on the true implementation, that is, how you can make an implementation, but abstraction focuses on what the implementation can do for you. But these concepts are interrelated. So, for a nice abstraction, your implementation should be properly encapsulated.

Grady Booch, in his famous book Object-Oriented Analysis and Design with Applications (Third Edition, Addison-Wesley), says the following: “Abstraction focuses on the observable behavior of an object, whereas encapsulation focuses on the implementation that gives rise to this behavior. Encapsulation is most often achieved through information hiding (not just data hiding), which is the process of hiding all the secrets of an object that do not contribute to its essential characteristics.”

You can revisit the Chapter 1 for these definitions.

9.2 Which one is faster between these—compile-time polymorphism or runtime polymorphism?

In general, if you resolve a call (for example, invocation of a method) early, it is faster. This is why you can conclude that compile-time binding is faster than runtime binding or polymorphism—because it is known in advance which method to call.

9.3 You told us earlier that inheritance might not always provide the best solution. Can you please elaborate?

In some cases, composition can provide a better solution. But to understand composition, you may need to know these concepts:
  • Association

  • Aggregation

Association can be one way or both ways. When you see this kind of UML diagram, it means ClassA knows about ClassB, but the reverse is not true.

../images/433474_2_En_9_Chapter/433474_2_En_9_Figa_HTML.jpg

The following diagram indicates a two-way association because both classes know each other.

../images/433474_2_En_9_Chapter/433474_2_En_9_Figb_HTML.jpg

Consider an example. In a college, a student can learn from multiple teachers, and a teacher can teach multiple students. There is no ownership in this kind of relationship. So, when you represent them with classes and objects in programming, you can say that both kinds of objects can be created and deleted independently.

Aggregation is a stronger type of association. The aggregation between a professor and department can be represented as follows.

../images/433474_2_En_9_Chapter/433474_2_En_9_Figc_HTML.jpg

Let’s go deeper. Suppose that Professor X submits his resignation letter to his existing organization to join a new organization. Although both Professor X and his former institution can survive without each other, Professor X needs to associate himself with a department in an institution. In this situation, you’d say that the department is the owner of this relationship and the department has professors.

Similarly, you can say that a human body has hands, a car has seats, a bike has tires, and so forth.

Note

In general, you say that a department has a professor. This is why an association relationship is also known as “has-a” relationship. (You can note down the key difference with inheritance here. Inheritance is associated with the “is-a” relationship.

Composition is a stronger form of aggregation, and this time you have a filled diamond in place.

../images/433474_2_En_9_Chapter/433474_2_En_9_Figd_HTML.jpg

A department in a college cannot exist without the college. The college only creates or closes its departments. (You can argue that if there is no department at all, a college cannot exist, but you do not need to complicate things by considering this type of corner case.) In other words, the lifetime of a department entirely depends on its college. This is also known as a death relationship because if you destroy the college, all of its departments are destroyed automatically. Similarly, you can say the hands (or legs, etc.) of a human being cannot exist without the body.

Revisiting the Diamond Problem

To show the power of aggregation/composition, let’s revisit the diamond problem that was discussed in Chapter 4, and then analyze the following program. Let’s start with the following code.
class Parent {
       public void show() {
             System.out.println("I am in Parent");
       }
}
class Child1 extends Parent {
       public void show() {
             System.out.println("I am in Child1");
       }
}
class Child2 extends Parent {
       public void show() {
             System.out.println("I am in Child2");
       }
}
Java does not allow you to write something like the following:
class GrandChild extends Child1,Child2// Error: Not supported in Java
{
       public void show() {
             System.out.println("I am in Grandchild");
       }
}

Demonstration 1

Now, let’s see how to handle this situation with aggregation (a lighter form of composition). Consider the following code:
package java2e.chapter9;
class Parent {
       public void show() {
             System.out.println("I am in Parent");
       }
}
class Child1 extends Parent {
       @Override
       public void show() {
             System.out.println("I am in Child1");
       }
}
class Child2 extends Parent {
       @Override
       public void show() {
             System.out.println("I am in Child2");
       }
}
//Not supported in Java
/*
 * class GrandChild extends Child1,Child2// Error: Not supported in Java {
 * public void show() { System.out.println("I am in Grandchild"); } }
 */
class GrandChild {
       Child1 ch1 ;
       Child2 ch2 ;
       GrandChild() {
             ch1 = new Child1();
             ch2 = new Child2();
       }
       public void showFromChild1() {
             ch1.show();
       }
       public void showFromChild2() {
             ch2.show();
       }
}
And here is code for Demonstration1.java which contains the main() method:
class Demonstration1 {
    public static void main(String[] args) {
        System.out.println("***Demonstration-1.The concept of aggregation/composition to handle the diamond Problem*** ");
        GrandChild gChild = new GrandChild();
        gChild.showFromChild1();
        gChild.showFromChild2();
    }
}
Output:
***Demonstration-1.The concept of aggregation/composition to handle the diamond Problem***
I am in Child1
I am in Child2

You can see that both Class1 and Class2 have overridden their parent’s show() method. And the Grandchild class doesn’t have its own show() method. Still, you can invoke those class-specific methods through the Grandchild object.

The Grandchild class allows you to create the objects from both Class1 and Class2 inside its constructor body. In the prior example, though the Child1 and Child2 objects can survive without the Grandchild objects, there are no individual objects from either Class1 or Class2. So, in this implementation, if Grandchild objects are not present in your application (let’s say, have been garbage collected), there will be no Class1 or Class2 object that can reside inside the system. You can also place some restrictions on users so that they are not able to create objects of Class1 and Class2 directly inside the application; for simplicity, I have ignored that part.

Note

You are aware of generalization, specialization, and realization. You have used these concepts in your applications. When your class extends another class (i.e., inheritance), you use the concepts of generalization and specialization; for example, a footballer (a.k.a. a soccer player) is a special kind (specialization) of athlete. Or, you can say that both a footballer and a basketball player are athletes (generalization). And when your class implements an interface, you use the concept of realization.

Q&A Session

9.4 In Demonstration 1, inside the client code, I can instantiate a Child1 or Child2 object. For example, I can use the following line of code:
Child1 child1=new Child1();

And in such a case, a Child1 object can persist in my system without a GrandChild object. Is this understanding correct?

Yes. You can say that this is an example of aggregation, or a lighter form of composition. But you can always restrict the user to directly instantiating objects from the Parent, Child1, or Child2 classes. For example, you can place these classes inside a package and let your Grandchild class be the only public class inside that. Inside the client code, one can create GrandChild objects only.

Consider the structure in Figure 9-1, which is the Package Explorer view for the upcoming demonstration.
../images/433474_2_En_9_Chapter/433474_2_En_9_Fig1_HTML.jpg
Figure 9-1

Using composition to solve the diamond problem

And the classes inside the package (java2e.chapter9.mypackage) are as follows (key changes are shown in bold):
//Parent.java
package java2e.chapter9.mypackage;
class Parent {
       public void show() {
             System.out.println("I am in Parent");
       }
}
//Child1.java
package java2e.chapter9.mypackage;
class Child1 extends Parent {
       @Override
       public void show() {
             System.out.println("I am in Child1");
       }
}
//Child2.java
package java2e.chapter9.mypackage;
class Child2 extends Parent {
       @Override
       public void show() {
             System.out.println("I am in Child2");
       }
}
//GrandChild.java
package java2e.chapter9.mypackage;
public class GrandChild { //This class is public here
       Child1 ch1 ;
       Child2 ch2 ;
       public GrandChild() {
             ch1 = new Child1();
             ch2 = new Child2();
       }
       public void showFromChild1() {
             ch1.show();
       }
       public void showFromChild2() {
             ch2.show();
       }
}
The client code may look like the following:
//Demonstration1.java
package java2e.chapter9;
import java2e.chapter9.mypackage.GrandChild;
class Demonstration1 {
       public static void main(String[] args) {
             System.out.println("***Demonstration-1.The concept of aggregation/composition to handle the diamond Problem*** ");
             //Child1 child1=new Child1();//Error: not visible to client
             //Child2 child1=new Child2();//Error: not visible to client
             GrandChild gChild = new GrandChild();
             gChild.showFromChild1();
             gChild.showFromChild2();
       }
}

Now, if you execute the program, you’ll receive the same output, but in this structure you allow an outsider to create only a Grandchild object. So, in such a case, you provide more restriction, and your application will not hold any object from Child1 or Child2 if there is no GrandChild object.

9.5 What are the challenges and drawbacks of OOP?

Many experts believe that, in general, the size of object-oriented programs is larger. Due to the larger size, you may need more storage (but nowadays, these issues hardly matter).

Some developers find difficulties in the object-oriented programming style. They may still prefer other approaches, such as structured programming, logic programming, and so forth, so if they are forced to work in an OOP environment, life becomes tough for them.

It is also true that you cannot efficiently solve every real-world problem in the object-oriented style. There are always some problems that can be solved better with a different approach; for example, a particular sudoku puzzle can be solved much more easily with Prolog (a logic programming language) than with Java (an object-oriented programming language).

Also, a common problem with the object-oriented style may arise when you need to find bugs in an execution flow, particularly if, in your codebase, there are many small methods (or functions) that call each other for a simple event. However, I personally like object-oriented programming because I believe that its merits are greater than its demerits.

Summary

This chapter included the following:
  • A quick review of the core OOP principles in this book

  • How to differentiate abstraction from encapsulation

  • How to implement the concept of composition/aggregation in your application

  • The challenges and drawbacks associated with OOP

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

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