What is object-orientation after all? Or the only thing to watch out for in programming
Lamron
Posted on November 21, 2022
Before the main topic
First of all, I will give an example for complete beginners who only know the word "object-oriented" so that they can get an idea of it. Please note that this is just an example and incomplete. The language is Java, but I write it so that you can understand even if you don't know Java.
Object-oriented programming is (to put it in immediate terms) programming data and related methods together in a class.
For example, consider the following Friend
class for managing information about friends.
class Friend {
String name;
String phoneNumber;
String mailAddress;
Friend(String name, String phoneNumber, String mailAddress) {
//"this" means itself.
this.name = name;
this.phoneNumber = phoneNumber;
this.mailAddress = mailAddress;
}
void outputContacts() {
System.out.println(name);
System.out.println("Phone Number:" + phoneNumber);
System.out.println("Email:" + mailAddress);
}
}
This class has three fields (member variables): name
, phoneNumber
, and mailAddress
, and a method outputContacts()
that uses them.
The method Friend(String name, String phoneNumber, String mailAddress)
with the same name as the class name is called constructor and used as follows.
Friend tanaka = new Friend("Ichiro Tanaka", "03-444-444", "tanaka@qmail.com");
As you can see from the Friend constructor, this code sets the name
field to "Ichiro Tanaka", the phoneNumber
field to "03-444-4444", and the mailAddress
field to "tanaka@qmail.com".
Then call the method as follows to display the contact information.
tanaka.outputContacts();
/*
Ichiro Tanaka
Phone number: 03-444-4444
Email: tanaka@qmail.com
*/
In this way, a class is a design that defines "what information it should have" (in this case, name, phone number, and e-mail address) and "what function it should have" (in this case, output of contact information), and a class that is actually materialized by calling a constructor with new
with the data is called an instance.
One of the advantages of creating classes is that you can create a number of data and functions all in one place. If you don't use classes, and you have a bunch of friends, you end up with code like this
String tanakaName = "Ichiro Tanaka";
String tanakaPhoneNumber = "03-444-444";
String tanakaMailAddress = "tanaka@qmail.com";
String yamadaName = "Taro Yamada";
String yamadaPhoneNumber = "03-555-555";
String yamadaMailAddress = "yamada@qmail.com";
String suzukiName = "Hanako Suzuki";
String suzukiPhoneNumber = "03-666-666";
String suzukiMailAddress = "suzuki@qmail.com";
//continues below...
System.out.println(tanakaName);
System.out.println("Phone number: "+tanakaPhoneNumber);
System.out.println("Mail: "+tanakaMailAddress);
System.out.println(yamadaName); System.out;
System.out.println("Phone number: "+yamadaPhoneNumber); System.out;
System.out.println("mail: "+yamadaMailAddress);
System.out.println(suzukiName); System.out;
System.out.println("phone number: "+suzukiPhoneNumber); System.out;
System.out.println("mail: "+suzukiMailAddress);
//continues below...
It is hard to understand and troublesome.
Friend tanaka = new Friend("Ichiro Tanaka", "03-444-444", "tanaka@qmail.com"); Friend yamada = new Friend("Taro Yamada", "03-555-555", "tanaka@qmail.com")
Friend yamada = new Friend("Taro Yamada", "03-555-555", "yamada@qmail.com")
Friend suzuki = new Friend("Hanako Suzuki", "03-666-666", "suzuki@qmail.com");
//continued below...
tanaka.outputContacts();
yamada.outputContacts();
suzuki.outputContacts();
//continued...
It would be clearer if you could write.
Introduction
Now it is time to explain the core of object-oriented programming, but even though this object-oriented programming has been around for quite a long time and is still used by many people, it is still not very clear what it means. One page says that it has elements of "encapsulation," "inheritance," and "polymorphism". But the object-orientation proponent says it is messaging. Wikipedia, the history goes on for a long time and says there are several types.
However, what I have learned over the years of programming, both for pleasure and work, is that object-oriented programming, or rather, the discourse of "programming should be ________" exists for one purpose, and once you understand it, programming techniques such as object orientation and "use easy-to-understand variable names" are easily derived.
Readable code
When I started programming as a hobby, I didn't understand why it was necessary to create something object-oriented. Also, although I understood the cautionary notes such as "Use easy-to-understand variable names", I didn't think it was important. As a matter of fact, you don't need these for short, personal use programs. For example, if you want to batch change the file names in a certain folder (add [read] to the beginning), this code is sufficient.
File a = new File("C:\\Users\\admin\\Documents\\ss");
for(File f : a.listFiles()){
File b = new File(a, "[read]"+f.getName());
f.renameTo(b);
}
No need to create objects. You don't even need to use meaningful variable names. It's just a hassle because you only need to use it once and you'll know it yourself.
The reason why object-oriented programming and variable naming are controversial is because they are aimed at programs that are developed by a long term, multi-person team. Programs developed in the course of business are created by a large number of people in a large company and are adjusted over a period of years. That is why the code must be understandable by others and loosely coupled (see below) so that changes will not affect unexpected areas.
If you think "I'm just a hobbyist, so it doesn't matter to me", you're wrong. Even if there is only half a year gap, you will have forgotten the meaning of the code you wrote in the past, so you still need to write code that is easy to understand and loosely coupled.
In short, programming techniques such as object-orientation and "use easy-to-understand variable names" are for "writing code that is easy for others (including future self) to read and write".
What is Object Orientation in the context?
To explain it in my own way, object-orientation is to create a group (class) of data and functions while following rules such as encapsulation in order to "write code that is easy for others (including future self) to read and write".
Let's take a look at Java's ArrayList class as an example.
List<String> list = new ArrayList<String>();
list.add("C");
list.add("A");
System.out.println(list.get(0));//C
Even if you don't know Java, you can see that "C" and "A" are added to list
and the 0th element is gotten. Like this, it is important to be able to understand what the code means without looking at the contents of the class, and everything I explain below is for this purpose. Because it is impossible to understand every single line of code in a large company's program, which can be millions of lines of code (and of course it is preferable to have less code to understand in a small program as well).
Encapsulation
The most important feature of object-oriented programming is encapsulation. This means "making visible only what is necessary for the users and hiding the rest". In programming language terms, this means setting public and private appropriately (in Python, normal variables are "public" and "private" when prefixed with "__"(two underscores)).
For example, the ArrayList class actually has internal methods such as ensureCapacityInternal() and grow() that perform complex processing, but they are "private"(non-public) because they are not relevant to the list user. Only the methods which users need, such as add() and get(), are "public". This makes ArrayList an easy and safe class to use without knowing its internals.
To put it more simply, take, for example, a calculator. Do you know how the electronic circuits inside a calculator work?
Most people do not. But anyone can use a calculator. This is because the internal electronics are "private" and the buttons and display screen are "public". What if the calculator is not covered by a case and the electronic circuits are exposed? If you are very knowledgeable about electronic circuits, you might be able to modify the calculator and make it your own special calculator. However, most people can't understand them, and would most likely touch it by accident and cause a malfunction. That is why only the parts that accept external operations are made "public", while the internal parts, such as electronic circuits, that do not need or should not be touched directly are kept "private". This is encapsulation.
If you dare to write a calculator in a programmatic way, it would look like this.
public class Calculator{
/** Button "1" is public */
public void button1(){
//processing
}
/** Button "2" is public */
public void button2(){
//Processing
}
... // continued
/** Internal calculations are private */
private double calculate(){
//process
}
/** Result display is public */
public double showResult(){
//process
}
}
If you think in this way, you can see that "encapsulation" exists everywhere in everyday life. You can operate your TV without knowing what signals your TV remote controller emits. You can drive a car without knowing the blueprint of the engine. Even if you don't know the details of the year-end tax adjustment, you can get it just by submitting the documents to the accounting department.
In this way, by making it a black box where "you can know what results will be returned even if you don't know the internals", we can live in a complex society, and large-scale programs can be easily understood.
I mentioned in the "Introduction" that the proponent of object-oriented programming refer to it as "messaging", and the key is that you can program without knowing the internals by simply sending a "message" to an "object" and receiving the "message" that comes back.
Tightly coupled and loosely coupled
Tight coupling is when the connection between elements is strong, and loose coupling is when the connection between elements is weak. Generally speaking, it is better to group tightly coupled elements into a class and to have loosely couple classes.
For example, what if a calculator class has code for calculating dates? You might think, "Why not have a class that can do everything!". However, date calculations are quite different from normal numerical calculations, such as calculating dates separately, calculating a day of the week, and taking leap years into account. Therefore, if you have a calculator class that also has code for date calculations, when you edit the class internals, you will find unrelated methods and variables each other that will confuse you. The same goes for the users. It is better to divide it into a Calculator class and a Date Calculator class obediently.
The classes should be loosely coupled. For example, if the state of the Calculator class affects the behavior of the Date Calculator class, it may affect the Date Calculator class even though you only intended to fix the Calculator class, resulting in unexpected bugs. If you really want to change the behavior of the Date Calculator class depending on the state of the calculator class, create a method to get the necessary parameters from the Calculator class and create a method to receive them in the Date Calculator class to "visualize" the linkage.
Polymorphism
Polymorphism is the ability to change the internal processing in many ways while keeping the external surface the same. Consider, for example, a TV remote controller. Various companies such as Sony and Samsung make televisions, and although internal electronic circuits of remote controllers are different, they all have the channel switching button and volume up/down button, and the operation is the same. Polymorphism means that even if the internal implementations are different, the external aspects such as "what functions they have" can be shared.
If you want to write a remote controller like a program, you can use the following interface.
public interface RemoteController{
public void channel1();
public void channel2();
...//continued
public void volumeUp();
public void volumeDown();
...//continued
}
In this way, the author can tell the writers, "You can change the internal implementation, but please promise to have these function". And the users can use it as a remote controller without worrying about which manufacturer it is.
In terms of specific programs, for example, Java's ArrayList class and HashSet class have quite different ways of holding elements, but both inherit the Collection interface, so regardless of internal implementation you can see that you can get the elements in order with iterator() method.
By the way, if you use an object-oriented language, you may think "I understand interface inheritance, but what about class inheritance?". However, class inheritance is not recommended because it can cause unexpected behavior if you do not know the details of the parent class, or if the hierarchy is too deep, variables may be scattered in many layers, making it difficult to track them, or variables with the same name cause confusion. In fact, even the creator of Java says that class inheritance (extends) should be avoided as much as possible.
Practice
Now, let's try a simple object-oriented programming based on the above. The language is Java, but I will write it so that those who do not know Java can understand it. What we will create is a program that calculates "duration". For example, if you work for "2 months and 18 days" and then work for "1 month and 4 days," the program will output that you have worked for a total of "3 months and 22 days".
However, 30 days is converted to 1 month. In other words, if you work "1 month 24 days" and "3 months 15 days", the total is "4 months 39 days", but since 30 days is converted to 1 month, you subtract 30 from the number of days and add 1 to the month to output "5 months 9 days".
If you write a program without object orientation, it will be as follows (in Java, division between integers returns only the integer part).
int monthSum = 0;
int daySum = 0;
//work for a month and 24 days
monthSum = monthSum + 1;
daySum = daySum + 24;
//// Convert 30 days to 1 month
monthSum = monthSum + daySum/30;
daySum = daySum%30;
//work for 3 months and 15 days
monthSum = monthSum + 3;
daySum = daySum + 15;
//// Convert 30 days to 1 month
monthSum = monthSum + daySum/30;
daySum = daySum%30;
System.out.println((monthSum/12)+" years "+(monthSum%12)+" months "+daySum+" days");//0 years 5 months 9 days
This still does the calculation, but the code is messy and bugs may occur due to copy-paste mistakes. So let's use the Duration class to make the following code.
Duration durationSum = new Duration(0, 0);
//work for a month and 24 days
Duration duration1 = new Duration(1, 24);
durationSum.add(duration1);
//work 3 months 15 days
Duration duration2 = new Duration(3, 15);
durationSum.add(duration2);
// toString() is automatically called
System.out.println(durationSum);//0 years 5 months 9 days
What do you think? I think it's a clean code that you can understand just by reading it.
The Duration class itself is following.
/** DAY_OF_MONTH days are automatically converted to 1 month. */
public class Duration {
/** number of days to be considered as one month */
public static final int DAY_OF_MONTH = 30;
// make it private so that users cannot change it
private int monthNum;
private int dayNum;
public Duration(int monthNum, int dayNum){
//DAY_OF_MONTH days is converted to one month
this.monthNum = monthNum + dayNum/DAY_OF_MONTH;
this.dayNum = dayNum % DAY_OF_MONTH;
}
public void add(Duration duration){
this.monthNum = this.monthNum + duration.getMonthNum();
this.dayNum = this.dayNum + duration.getDayNum();
//DAY_OF_MONTH days is converted to one month
this.monthNum = this.monthNum + this.dayNum/DAY_OF_MONTH;
this.dayNum = this.dayNum % DAY_OF_MONTH;
}
// Make them public so that users cannot set them but can retrieve them
public int getMonthNum(){
return monthNum;
}
public int getDayNum(){
return dayNum;
}
@Override
public String toString(){
return (monthNum/12)+" years "+(monthNum%12)+" months "+dayNum+" days";
}
}
It is written in the code, but I will add explanations.
First of all, since this class represents a period of time, it keeps monthNum
and dayNum
fields. However in the constructor and add()
method, it is controlled so that if dayNum
exceeds 30, it is automatically converted to one month. Therefore, the dayNum
field is kept private to prevent users from breaking this control and putting a number greater than 30 in the dayNum
field. On the other hand, since there is no reason to prohibit users to obtain the data, the getMonthNum()
and getDayNum()
methods are public(encapsulated).
Next, since "the number of days considered as one month" is fixed to 30 days regardless of the period, DAY_OF_MONTH
is final and static. And we want the users to know how many days are converted into one month, so we set them to public.
Finally, we implement the toString()
method. All classes in Java inherit Object
class, and toString()
is a method of it. By implementing this, even classes you create will be displayed that is easy to understand for humans in debug mode or System.out.println(obj);
. It's a kind of polymorphism because the toString()
method is common and how they are displayed is different depending on the classes.
Finally
As I wrote at the beginning, the important thing in programming is "writing code that is easy for others (including future self) to read and write". Not only object-orientation, but when learning programming techniques and writing programs, please think about "Is it really code that others (including future self) can read and modify easily?".
Posted on November 21, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 21, 2022