11 Object Oriented Diagram Classes From Life

11 Object-Oriented - Diagram Classes from Life #

Hello, I am Jingxiao.

Many friends started learning programming with C++ or JAVA. After struggling to understand the basic data types, assignment, conditionals, and loops, they are immediately faced with an overwhelming wall called OOP (object-oriented programming). They dive into a sea of proprietary terms such as public and private access modifiers, multiple inheritance, polymorphic derivation, pure functions, abstract classes, and friend functions, and can’t find a way out. As a result, they give up on advancing in programming.

In comparison, Python is a relatively friendly language. From the very beginning, it encouraged lightweight and interactive programming. In theory, Python’s imperative language is Turing complete, which means an imperative language theoretically can do everything any other language can do. To take it further, using only the MOV instruction in assembly language, Turing complete programming can be achieved.

So why not do it this way? In fact, this is what programmers did in the “ancient times”. However, as program complexity gradually increased along with iterative demands, modifying old code became incredibly cumbersome. It became impossible to iterate and maintain the code without causing a ripple effect throughout the entire system. In some cases, starting from scratch would be the only option, which is also why much of the old code is considered a “mountain of crap”.

Traditional imperative languages have countless repetitive code. Although the creation of functions alleviates much of the repetition, with the development of computers, functions alone are not enough. It requires introducing more abstract concepts into computers to mitigate (not solve) this problem. Hence, OOP came into existence.

Since its creation by a programmer in 1989, Python has rapidly evolved from a basic scripting language. It can now be used to write system programs, large-scale projects, data science computations, and artificial intelligence. However, it still needs to incorporate excellent designs from other languages. We must pay a certain price to master object-oriented programming in order to overcome the bottleneck period in the learning process and move forward.

In the next two lessons, I will explain object-oriented programming from basics to real-world applications. In the first lesson, I will quickly and clearly explain the fundamentals to ensure you can quickly grasp the basic principles of OOP. In the second lesson, we will start from scratch to build a search engine, integrating the knowledge we learned in the previous lesson.

The content may be different from all the tutorials you have seen before. I will try to examine these difficult points from the perspective of a beginner. At the same time, we will focus on practical applications and engineering. We do not aim to cover everything extensively, but we will provide sufficient outlines of the core concepts. I can guarantee that the content will be clear and understandable, but to truly master it, you still need to read and think attentively. Real improvement can only be accomplished by yourself.

Objects, have you found them? #

Let’s start by learning the most basic concepts in object-oriented programming.

To help you understand the abstract concepts, let me give you an analogy. In biology class, we learned about the concept of “kingdom, phylum, class, order, family, genus, species,” where scientists classify various animals, plants, and microorganisms based on their similarities. This classification helps with research. In our daily life, we also categorize things around us:

  • Both cats and dogs are animals.
  • Both lines and circles are geometric shapes in 2D.
  • “Harry Potter” and “A Song of Ice and Fire” (also known as “Game of Thrones”) are both novels.

Naturally, similar things share similar characteristics:

  • Animals can move.
  • 2D shapes have area and perimeter.
  • Novels have authors and storylines.

Now let’s talk about Python and see how these concepts correspond to it. Here, we’ll start with a piece of basic Python code to introduce you to object-oriented programming. Don’t be overwhelmed by its length, you don’t have to understand all the code immediately. Follow along with me, and I’ll explain it step by step.

    class Document():
        def __init__(self, title, author, context):
            print('init function called')
            self.title = title
            self.author = author
            self.__context = context # Properties starting with __ are private properties
    
        def get_context_length(self):
            return len(self.__context)
    
        def intercept_context(self, length):
            self.__context = self.__context[:length]
    
    harry_potter_book = Document('Harry Potter', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')
    
    print(harry_potter_book.title)
    print(harry_potter_book.author)
    print(harry_potter_book.get_context_length())
    
    harry_potter_book.intercept_context(10)
    
    print(harry_potter_book.get_context_length())
    
    print(harry_potter_book.__context)

Let’s go through this code, and I’ll explain a few concepts.

  • Class: A collection of similar things, which corresponds to the class in Python.
  • Object: An entity in a collection, corresponding to a particular object generated by a class, such as harry_potter_book in the code.
  • Attribute: A static feature of an object, such as the title, author, and __context mentioned in the code.
  • Function: A dynamic ability of an object, such as the intercept_context() function mentioned in the code.

Of course, these explanations are not rigorous or complete, but if you have no knowledge of object-oriented programming, they can help you quickly gain an intuitive understanding.

Allow me to digress for a moment. I recall when I participated in a math competition, I once discussed math learning with a math prodigy. I remember that we had similar views on mathematics: many mathematical concepts are very abstract, and if you approach them purely from logical reasoning rather than a higher perspective, it’s easy to get stuck. Concrete, intuitive imagination and analogies are the key to quickly unlock the doors of mathematics. Although these imaginations and analogies are not rigorous or complete, and often even incorrect or fanciful, they can indeed help us quickly find the right door.

Just like the curiosity many people have: “How did the top student come up with that answer?” The German mathematician Klein once said, “What propels mathematics forward is not so much rigorously proven theorems but rather those mathematicians who have strong intuition.” The same applies to the programming world. If you are not satisfied with being just a CRUD “code monkey” but want to become an outstanding engineer, you must actively develop your intuitive thinking and quick analogical reasoning skills, especially when you cannot find a bug. This is the fastest method and path to make progress in programming learning.

Returning to our topic, let’s continue with a more rigorous definition of classes based on the code we just discussed.

A class is a collection of objects that have the same attributes and functions.

Although this may seem somewhat circular (lol), it is important to emphasize repeatedly so that you have a more realistic understanding of the most fundamental principles of object-oriented programming. After firmly grasping this point, let’s further analyze the code we just mentioned. To facilitate your reading and learning, I have placed it below this paragraph.

class Document():
    def __init__(self, title, author, context):
        print('init function called')
        self.title = title
        self.author = author
        self.__context = context # The attribute starting with __ is private

    def get_context_length(self):
        return len(self.__context)

    def intercept_context(self, length):
        self.__context = self.__context[:length]

harry_potter_book = Document('Harry Potter', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')

print(harry_potter_book.title)
print(harry_potter_book.author)
print(harry_potter_book.get_context_length())

harry_potter_book.intercept_context(10)

print(harry_potter_book.get_context_length())

print(harry_potter_book.__context)

########## Output ##########

init function called
Harry Potter
J. K. Rowling
77
10

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-b4d048d75003> in <module>()
     22 print(harry_potter_book.get_context_length())
     23 
---> 24 print(harry_potter_book.__context)

AttributeError: 'Document' object has no attribute '__context'

As you can see, the class Document defines the Document class, and we can see that it has three functions, which are the three functions of the Document class.

Among them, __init__ represents the constructor, which is a function that is automatically called when an object is created. We can see that when the line harry_potter_book = Document(...) is executed, the string 'init function called' will be printed. get_context_length() and intercept_context() are ordinary functions of the class; we call them to perform some tasks on the object’s attributes.

The class Document also has three attributes: title, author, and __context, which are passed in through the constructor. The code here is straightforward, and we can see that intercept_context() can modify the __context attribute of the harry_potter_book object.

The only thing that needs to be emphasized is that if an attribute starts with __ (note that there are two underscores), we assume that this attribute is a private attribute. A private attribute refers to an attribute that we do not want to be accessed or modified outside the class’s functions. Therefore, you can see that title and author can be freely printed, but print(harry_potter_book.__context) will throw an error.

Teacher, can you be more powerful? #

Once you have mastered the most basic concepts, you can actually do a lot. However, as complexity increases in engineering practice, you may encounter some questions:

  • How can you define constants in a class that can be easily accessed by each object without having to reconfigure them?
  • If a function does not involve accessing or modifying the attributes of a class, but placing it outside the class seems inappropriate, how can you do it more elegantly?
  • Since a class is a collection of similar objects, can it also be a collection of similar classes?

The first two questions are easy to solve, but they involve some commonly used code conventions. Here is a code example. Similarly, you don’t need to read this entire code at once. Just follow my pace and learn slowly.

class Document():

    WELCOME_STR = 'Welcome! The context for this book is {}.'

    def __init__(self, title, author, context):
        print('init function called')
        self.title = title
        self.author = author
        self.__context = context

    # Class method
    @classmethod
    def create_empty_book(cls, title, author):
        return cls(title=title, author=author, context='nothing')

    # Instance method
    def get_context_length(self):
        return len(self.__context)

    # Static method
    @staticmethod
    def get_welcome(context):
        return Document.WELCOME_STR.format(context)


empty_book = Document.create_empty_book('What Every Man Thinks About Apart from Sex', 'Professor Sheridan Simove')

print(empty_book.get_context_length())
print(empty_book.get_welcome('indeed nothing'))

########## Output ##########

init function called
7
Welcome! The context for this book is indeed nothing.

For the first question, in a Python class, you only need to declare and assign it parallel to functions. For example, the WELCOME_STR in this code. A common practice is to use all uppercase letters to represent constants. Therefore, we can express this string in the class using self.WELCOME_STR or outside the class using Entity.WELCOME_STR.

Regarding the second question, we introduce the concepts of class methods, instance methods, and static methods. They are actually easy to understand. The effects of the first two are dynamic, allowing access to or modification of object attributes. In contrast, static methods are not associated with the class, and the most obvious characteristic is that the first parameter of a static method has no special significance.

Let’s take a closer look at these types of functions. Generally, static methods can be used to perform simple independent tasks, making testing easier and optimizing code structure. Static methods can also be indicated by adding @staticmethod before the function. There are corresponding examples in the code. This uses the concept of decorators, which will be discussed in detail in later chapters.

The first parameter of a class method is generally cls, indicating that you must pass a class. The most common use of class methods is to implement different __init__ constructors. For example, in the code mentioned earlier, we use the create_empty_book class method to create a new book object where context is always 'nothing'. This code is clearer than directly creating an object. Similarly, class methods need to be declared with the decorator @classmethod.

Instance methods are the most common types of functions in a class. They do not require any decorator declaration, and the first parameter self represents the reference to the current object. Through this function, you can implement the desired query/modification of class attributes and other functions.

Inheritance, the Dream of Every Trust Fund Baby #

Next, let’s move on to the third question: if a class is a collection of similar objects, can it also be a collection of similar classes?

The answer is, of course it can. As long as it is abstracted well, a class can describe a collection of any thing. Of course, you need to carefully and rigorously define it, otherwise you might accidentally cause the third mathematical crisis XD.

Inheritance of a class, as the name suggests, means that a class has both the characteristics of another class and unique characteristics different from the other class. In this case, the first class is called a subclass, and the other is called a superclass. Characteristics are actually the attributes and functions of the class.

class Entity():
    def __init__(self, object_type):
        print('parent class init called')
        self.object_type = object_type
        
    def get_context_length(self):
        raise Exception('get_context_length not implemented')
        
    def print_title(self):
        print(self.title)

class Document(Entity):
    def __init__(self, title, author, context):
        print('Document class init called')
        Entity.__init__(self, 'document')
        self.title = title
        self.author = author
        self.__context = context
        
    def get_context_length(self):
        return len(self.__context)

class Video(Entity):
    def __init__(self, title, author, video_length):
        print('Video class init called')
        Entity.__init__(self, 'video')
        self.title = title
        self.author = author
        self.__video_length = video_length
        
    def get_context_length(self):
        return self.__video_length

harry_potter_book = Document('Harry Potter(Book)', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')
harry_potter_movie = Video('Harry Potter(Movie)', 'J. K. Rowling', 120)

print(harry_potter_book.object_type)
print(harry_potter_movie.object_type)

harry_potter_book.print_title()
harry_potter_movie.print_title()

print(harry_potter_book.get_context_length())
print(harry_potter_movie.get_context_length())

Let’s learn these concepts with the help of code. In this code snippet, Document and Video have similarities, they both have corresponding attributes such as title, author, and context. We can abstract a class called Entity to serve as the parent class for both classes.

Firstly, pay attention to the constructors. Each class has a constructor, and when a subclass inherits and creates an object, the constructor of the parent class is not automatically called, so you must explicitly call the constructor of the parent class in the __init__() function. The execution order is the constructor of the subclass -> the constructor of the parent class.

Next, pay attention to the get_context_length() function in the parent class. If you directly create an object using Entity and call the get_context_length() function, it will raise an error and interrupt the program execution. This is actually a good practice called function overriding, which requires the subclass to rewrite the get_context_length() function to override the original function.

Lastly, note the print_title() function. This function is defined in the parent class, but objects of the subclass can freely use it to print the title. This demonstrates the advantages of inheritance: reducing duplicate code and reducing the entropy (i.e. complexity) of the system.

By now, you should have a more detailed understanding of inheritance and have a basic grasp of object-oriented programming. Of course, if you want to reach a higher level, extensive practice in programming and learning more detailed knowledge are necessary.

Finally, I would like to expand on abstract functions and abstract classes for you, and I will also use a code snippet to explain.

from abc import ABCMeta, abstractmethod

class Entity(metaclass=ABCMeta):
    @abstractmethod
    def get_title(self):
        pass

    @abstractmethod
    def set_title(self, title):
        pass

class Document(Entity):
    def get_title(self):
        return self.title

    def set_title(self, title):
        self.title = title

document = Document()
document.set_title('Harry Potter')
print(document.get_title())

entity = Entity()

You should have noticed that Entity itself is not useful, it is only needed to define the basic elements of Document and Video. However, what if you accidentally create an object of Entity? To prevent such mistakes, I must introduce the concept of an abstract class.

An abstract class is a special kind of class that is born to be a superclass, and an error will occur if it is instantiated. Similarly, abstract functions are defined in the abstract class, and the subclass must override the function to use it. The corresponding abstract functions are indicated by the decorator @abstractmethod.

As we can see from the code, entity = Entity() results in an error, and only by inheriting Entity through Document can it be used properly.

This is actually an important concept in software engineering, defining interfaces. Large-scale projects often require collaboration among many developers. For example, at Facebook, after an idea is proposed, the development team and product team will hold a product design meeting. The product manager (PM) writes the product requirements document, and then iterates on it. The team leader (TL) writes the development document, which defines the rough functions and interfaces of different modules, how each module collaborates, unit testing and integration testing, online gray testing, monitoring and logging, and a series of development processes.

An abstract class is just such a presence, it is a top-down design pattern. You only need to describe what needs to be done and define the interface with a small amount of code, and then it can be handed over to different developers for development and integration.

Summary #

So far, we have been emphasizing one thing: object-oriented programming is an important concept in software engineering. Just like dynamic programming is an important concept in algorithms, it is not a very specific technique, but a reflection of comprehensive abilities and an important method of decoupling and modularizing large-scale projects. In practice, we need to think more, especially abstractly, in order to master this technique more quickly.

Looking back at today’s content, I hope you can answer the following two questions on your own as a summary of today’s material, and write them in the comment area.

The first question is, what are the four elements of object-oriented programming? What is their relationship?

The second question is, after talking about inheritance for so long, what is inheritance exactly? Can you express it in three words?

I’m not joking here. In many Facebook Launch Docs (deployment documents), they require summarizing your document in five words because your document is not only seen by your team, but may also go up to the VP or CTO, and you need to be concise and let them quickly understand what you want to express.

Thought Question #

Finally, I’ll leave you with a thought question. Since you can inherit a class to gain the functions and attributes of the parent class, can you inherit two classes? The answer is yes, and this is called multiple inheritance. So here’s the question.

When we use single inheritance, the order of execution of the constructors is well determined, that is, the child class -> parent class -> grandparent class -> … in a chain. However, what about multiple inheritance? For example, consider the following example:

 --->B---
A-      -->D
 --->C---

This type of inheritance is called diamond inheritance, where B and C inherit from A, and then D inherits from B and C, creating an object of class D. So, what is the order of constructor calls in this case?

Feel free to write down your answers and thoughts in the comments section, discuss with me, and also feel free to share this article with your colleagues and friends.