Procedural vs Object-oriented

Properties of entities

Suppose you have some entities in the real-world that you want to model in a program, for example, some students and some courses. You may write your program line-by-line. Consider the Java code snippet below:

In this code snippet, you modelled a student in the real-world as having 3 properties in your program, and you used 3 variables to represent them, namely, id, name, and gender.

Although there are only three lines of codes, actually, you've already done at least two different things. (1) You have used 3 variables to represent 3 properties of one student. That means you defined a student as having 3 properties, not 2 or 4 properties, but 3. (2) You stored the actual values of those properties in the variables, i.e., 101 in id, "Steve" in name, "m" in gender.

To wrap up, you:

  1. defined how a student should look like in your program;
  2. stored the actual data in variables.

Now, let's expand your code to adopt more students. The code becomes something like this:

As you can see, you modelled 10 students in your program. Now your program has 3 x 10 = 30 lines of codes.

So far, there are only student entities in your program. How about we want to model some courses too? If you want to model some courses, you may add more variables to your program:

Each course has 3 properties represented by variables. And you stored the actual values of the properties in those variables. If you have 10 courses that you want to process in your program, you add 3 x 10 = 30 more lines of codes to your program.

Behaviours of entities

Usually we need to design some methods (functions) that use our data to do some operations. For example, we want to do some calculation for students by using their data. To do so, we write a method for processing students. Now the whole program becomes something like this:

Based on the above-mentioned sections, it is not difficult to imagine that your program becomes longer and longer if you have more entities or if the entities have more information to be represented. What if you have 100 more entities and 10 more operations for each kind of entities? You program will become very long and difficult to maintain.

To wrap up, you:

  1. added more variables to the program if you have greater number of entities or more types of entities to represent;
  2. added more methods to the program if you have more operations to do.

Using methods

We've already talked about the modelling of the properties and the behaviours of entities. Now let's talk about how we use the methods on the data.

We have a method called method1ForStudent() in our program. We use this method like this:

We call the method by passing each entity's data to the method. As you can see, we use the method line-by-line. In each line, we pass a set of variables of one student to the method. It has no problem and it is very logical as long as we do things correctly.

However, what if we want to change the variable names or the data type of a property (e.g. gender → sex, String → char)? And what if we want to add one more property to every student (i.e. a student also has a date of birth)?

If we want to achieve this, we have to do several things:

  1. add one more variable for each student (student_n_dob);
  2. assign values to the variables (student_n_dob = ...);
  3. if we need to process the new property, we need to rewrite our method;
  4. change the lines that call the method to pass 4 variables.

This approach works well. But it is cumbersome and error-prone, since you need to insert lines at the appropriate positions and ensure that the variable names, values and data types are correct.

We try to expand the whole program and it looks like this:

To wrap up, you:

  1. defined how entities should look like in your program;
  2. stored the actual data of entities in variables;
  3. defined methods for doing operations on entities;
  4. passed the data of entities to methods and called the methods.

Problems

  1. If you have more entities or properties, your program becomes longer.
  2. If you want to change a property, you have to change it line-by-line and make sure the data is correct.
  3. If you want to change a method, you have to change the lines that call the method.
  4. When you call a method, you have to ensure that you pass a correct set of data to the method (e.g. id, name, gender, dob are of the same student).
  5. Different methods for different types of entities are placed together.
  6. All methods can be called by any types of entities. For example, you can pass a student's data to a course method (method1ForCourse(student_1_id, student_1_gender, student_1_name);).

Thinking in terms of objects

Actually, when we try to represent an entity in the real-world in a program, we have already considered it naturally in terms of an object. For example, a person has its height, weight, gender, and name. We use variables to represent those properties in our program, like:

When we have more than one entity and more than one type of entities, we give a more specific name or a prefix to variables, so that one entity can distinguish from other entities and one type of entities can distinguish from other types of entities. Look at the above-mentioned code again:

The number "1" in the variable names is used to denote that these variables belong to a specific student or course (i.e. student_1_id is Steve's ID, student_1_name is Steve's name). And the word "student" and "course" in the variable names are used to denote that these variables belong to a specific type of entities (i.e. course_1_id is a course ID but not a student ID).

In conclusion, even though we didn't use any classes or objects to represent entities, somewhat we have already used some means to separate different entities and classify different types of entities. That means, we have already thought in terms of objects naturally.

Procedural programming and object-oriented programming co-exist (personal opinions)

Java is an object-oriented programming language. But it doesn't mean OO programming languages or programmers compete with the counterparts of the procedural one. In fact, procedural programming and object-oriented programming co-exist. We can see this in two dimensions:

  1. In the real-world, many tasks can be done gracefully using procedural programming. Sometimes it's more efficient and convenient to use procedural programming for solving some problems. Many procedural programs are working well in all kinds of systems. At the same time, many large-scale systems are designed and implemented in an OO way. Those systems are well-organised and easy to be extended and maintained. Therefore, procedural programming and object-oriented programming co-exist.
  2. In an OO program, a large portion of the program is object-oriented. However, inside each object, there can be procedural codes that are used to do some simple operations. If all of the codes need to be strictly object-oriented, it would be an agony to write programs, since OO programming usually requires designing program structures. That means if we use OO everywhere, we need to design the program structures before we write codes even though we are doing some simple coding. Therefore, procedural programming and OO programming co-exist. They don't compete with each other.

For more about object-oriented, you can refer to the book "The Object-Oriented Thought Process (2019, Addison-Wesley)".

Notes

Easy terms

Invoke

  • invoke = call
  • invoke a method = call a method
  • invoke (verb), invocation (noun)

Reference

  • reference (n.) of an object = the location of an object in the memory (e.g. RAM).
  • reference (v.) an object = to refer to an object in the memory.

Abbreviations

  • var. = variable
  • ele. = element

Class

In order to understand the ideas of OOP, we have to clarify the terms related to OOP used in Java.

Class (explanation 1)

A class is used to model an entity in the real-world. An entity in the real-world has properties and behaviours. E.g., a student has its name, height, gender, date of birth (properties). A student can change its name. A student can enrol to a course (behaviours).

Class (explanation 2)

A class is a definition of an object, a blueprint of an object (how an object looks like), a contract of an object (how an object's data and methods are accessed).

Class (explanation 3)

A class is a type of an object. E.g., a Student object is from the Student class and is of the Student type.

Object (explanation 1)

A class is a definition or imagination of an object. An object is a concrete, detailed existence of a class in your program.

Object (explanation 2)

An object is of a class. An object belongs to a class. An object is created by the class's constructor. A class has no object before one object is created by the constructor. An object of a class exists only after the object has been created.

Instance (explanation 1)

An existing example of a class. A class is a definition of an instance. An instance is created based on a class (definition). An instance of a class exists only after the instance has been created.

Instance (explanation 2)

An instance is almost the same as an object. The terms are used interchangeably.

Relationships among class, type, object, and instance:

  • A Student object belongs to the Student class.
  • A Student instance belongs to the Student class.
  • A Student object is of Student type.
  • A Student instance is of Student type.
  • A Student object is an instance of the Student class.
  • (X) A Student instance is an object of the Student class (somewhat wrong).
  • The Student class defines the Student type.
What does a Class look like?

A class contains data fields (properties) of the class's objects.

A class contains methods (behaviours / actions) that the class's objects can perform.

Members of a class are its data fields and methods.

To create a class, write like this:

To generalise, the syntax looks like this:

Why superclass / subclass / inheritance?

Sometimes two kinds of entities are related. And if we change some properties of one entity, the other entity's properties may need to be changed accordingly.

Consider an example of cars and motorcycles. Suppose all vehicles all over the world have a speed limit of 70km/h. No vehicles in the world can exceed the 70-km-per-hour speed limit. In this case, we can give a property to cars and motorcycles like this:

Defining like this is totally fine. But what if the god of our world changes the speed limit again? Or what if there are 300 kinds of vehicles in the world? We have to change that property's value in every class and make sure that they are in correct values. For dealing with such a case, we better use class inheritance.

In class inheritance, we have a superclass (or a supertype) whose data fields and methods are inherited by its subclasses. The subclasses have its own data fields and methods and those inherited from the superclass. If the inherited data fields are changed in the superclass, those data fields will also be changed in the subclasses. Class inheritance can be used when there is an "is-a" relationship between two classes. For example, a Car is a Vehicle, and a Motorcycle is a Vehicle. We can design a Vehicle class and make it a superclass by letting the Car and Motorcycle classes inherit (extend) the Vehicle class.

The UML diagram below denotes the relationships:

For more about object-oriented, you can refer to the book "The Object-Oriented Thought Process (2019, Addison-Wesley)" and "Software Engineering (2016, Pearson)".

Drawing UML diagrams: draw.io

Concept of abstract class

What does "abstract" mean? The word "abstract" of abstract class means that a class has very little specific details. Sometimes a class has so little details that we can't even imagine what the objects of the class really is. So, it is called an abstract class.

Concept of concrete class

In contrast to an abstract class, a concrete class is a class that has enough details to represent an entity. A class that is detailed enough to create an object of that class is considered as a concrete class.

Why do we need abstract classes?

We need abstract classes basically because of design needs or programming needs. We don't necessarily need to use abstract classes to describe the world. But when we do programming, we want to make the program easy to be maintained, and easy for other people to extend and use it. That's why we would create some classes that are very abstract.

What is an abstract superclass?

Sometimes a superclass is so abstract that it cannot be used to create any specific instances. Such a class is referred to as an abstract class.

When you don't know how to implement a method in a superclass, the method should be an abstract method; or when you want to deliberately leave the implementation to the subclasses, the method should be abstract.

An abstract method in a superclass doesn't have implementation. Its implementation is provided by the subclasses.

A class that contains abstract methods must be defined as abstract. If you don't want a subclass to be abstract, you must implement all abstract methods inherited from the superclass.

Interface

What is an interface?

An interface is a class-like construct that contains only constants and abstract methods.

The intent of an interface is to specify common behaviours for objects of related or unrelated classes. E.g., Comparable.

Why interface?

A superclass and a subclass represent an "is-a" relationship (e.g., an Apple is a Fruit). However, not necessarily there is an "is-a" relationship between two objects (or classes). In the programming aspect, sometimes we want to denote that a class has a certain property. And because of that property, the class should share some common data fields or methods among other classes. And these classes should have a common label or a common mark for describing that they have some common properties. In this case, we can use interfaces.

For example, a Chicken is an Animal. But an Apple is not an Animal. However, they are both Edible. It is fine to create a superclass called Food and let Chicken and Apple to extend the Food class. But what if we want the Chicken class's superclass to be the Animal class, and the Apple class's superclass to be the Fruit class? In this case, they cannot have the same superclass.

In order to denote that the two classes have some common properties while they do not have an "is-a" relationship, we can use interfaces in Java.

By using an interface, we can mark that two unrelated classes, Chicken and Apple, are a type of "Edible". They are subtypes of Edible. Interface inheritance in Java is used to represent the "is-kind-of" relationship. An Apple is a kind of Edible. A Chicken is a kind of Edible. There are "is-kind-of" relationships between Chicken and Edible, and between Apple and Edible.