Before getting started with this section it is probably of value to offer a bit of perspective/advice. If the topic of object orientation (OO) is new to you don't be surprised if the material seems abstract and/or opaque. Join the club. Personally, I have not encountered a student who reported or demonstrated immediate and complete understanding of the concepts. Rather, study and time are essential to achieve depth and applied dexterity.
Four-to-six months of experience working with an object orientation language is typically required to develop moderate proficiency with OO. However, the fundamentals of the paradigm are reasonably elementary and can be demonstrated with uncomplicated code examples. My advice is to work through the examples in this course and resist the urge to understand everything initially. Be comfortable with not having complete understanding. Such is the human condition. The concepts will become clearer with time and experience. Enjoy the process and work with confidence while you wait for clarity to gradually, and sometimes abruptly, ensue.
Object orientation is a software development approach that considers the 'object' to be the center of attention. In OO, the object is also often referred to as the 'first class citizen', meaning it has the full cooperation of the language. Prior to OO, the most common approach to software development was procedural programming in which the procedure was the focus. OO became popular with industry development teams in the 1990's and has since retained its prevailing stature. The paradigm consists of three general activities: object-oriented analysis (OOA), object-oriented design (OOD), and object-oriented programming (OOP).
In large software development organizations, the three activities are often performed by different team members. OOA consists of experienced practitioners taking a high-level view of the problem landscape and translating the domain challenges into OO terminology. In this sense, the term 'domain' means area of focus or application operating space. For example, the fields of financial, medical, industrial, transportation, manufacturing, are all application domains. And, within each of those fields, multiple domains exist. For instance, within the financial field there are sub-domains such as: taxation, investment, auditing, and banking.
After the domain-specific, but broad perspective has been characterized using OO vocabulary, the OOD activity further refines the general product of OOA into program requirements specifications. Text documents and UML (unified modeling language) diagrams are often used to formally express object-oriented designs.
The text and UML representations, the results of the design process, are supplied to the programmers who then develop solutions per the requirements outlined in the design documents.
Let's now look more closely at the characteristics that define OO.
What is an Object?
The two most elemental components of OO are the object and the class. Even though the class must be written before an object can be created from it, let's consider the object first since it is the focus of OO and all operations revolve around the object.
I think an object can best be defined by what it contains. Objects can contain two things, functionality and data. And, oh yes, other objects. Functions and data that are contained within an object are known as 'object members'. In the image of a class below, notice the eight data members (a.k.a. fields or properties) and the five functions (a.k.a. methods). Methods are followed by ()'s. An object is created from a class which will be discussed shortly.
Functionality means that there are components (sections of code) of the object that perform operation(s) or action(s). In most OO programming languages, functions that are members of a class are known as methods. An object of type Aircraft may contain methods such as takeoff(), climb(), cruise(), descend(), and land(). The concept of object type is known as a 'class' and is discussed in the next section. In code, we access an object's methods using the 'dot operator'. For example, if we have a specific object (a.k.a an instance) named aircraft_one which is of the type Aircraft, the following are examples of accessing the methods using the dot operator: aircraft_one.takeoff(), aircraft_one.descend(), aircraft_one.land(). In those examples, we are instructing the aircraft_one object to run the code in the methods takeoff(), descend(), and land().
Objects also contain data. The example object built from the Aircraft class may contain data elements such as speed, altitude, capacity, souls_on_board, max_fuel, fuel_remaining, and max_range. At any given point in time (during program operation), the collective values of an object's data elements represent the object's current 'state'. This is analogous to the 'State of the Union' address during which the President reports the current 'state' of the country. The 'state' of an object is the current collective value of its data. Like method access, the dot operator is also used to access data members. So, continuing with the example above, aircraft_one.altitude is the value of the current altitude data. Even though data can be accessed this way, it is customary to use a 'getter' (a.k.a. accessor) method to access data. More on that, and other OO topics, in upcoming chapters.
What is a Class?
In the previous section, an object named aircraft_one was discussed. aircraft_one was of type Aircraft. Aircraft is the class from which aircraft_one was made. A class is commonly described as a 'blueprint'. Consider the blueprint of a building. The blueprint is a specification of implementation details but it is not an actual building. Using OO terms, the building itself would be known as the object and the blueprint would be the class.
In OOP, the class (blueprint) is simply a section of code that describes an object (building). The process of creating an object from a class is known as instantiating an object. That is, an instance of the class is created. Using our example above, Aircraft is a class and aircraft_one is the name of an object created from the Aircraft blueprint. Another object named aircraft_two could be instantiated from the Aircraft class. aircraft_two would contain its own state as defined by the contents of its data members.
When an object is instantiated, the interpreter allocates memory (RAM) to store that object. When that object is no longer needed, it will be cleared from memory by a process known as garbage collection. Garbage collection is an automatic process managed by the execution engine (PVM). It can be deliberately invoked by the programmer but that is rarely (if ever) needed in development.
In addition to the OO activities of analysis, design, and programming, object orientation is also known for the following attributes: inheritance, polymorphism, and encapsulation. These three attributes are often used as a test to determine if a language can be considered object-oriented. That is, if a language possesses all three attributes it is object-oriented. The three are generally viewed as the pillars of OO.
Inheritance is possibly the most essential and compelling characteristic of OO. Without it, OO would lose much of its allure. Inheritance consists of defining higher level classes upon which lower level classes can be derived. This approach facilitates reuse of common code. This hierarchical relationship is commonly referred to as parent:child, super-class (or just class):subclass, or base:derived. Let's use our Aircraft class as an example.
In the diagram below which is a UML depiction, the familiar Aircraft class is shown with its eight data members and five methods. There are also two subclasses shown: MilitaryAircraft and CommercialAircraft. They are the more specific types of aircraft. The subclasses 'inherit' the data members and methods of the Aircraft class. Each of the subclasses have an additional data member and method. So, both MilitaryAircraft and CommercialAircraft classes have nine data members and and six methods. The data members and methods of the Aircraft class are not shown in the subclasses but they do exist in each subclass.
Naturally, we will encounter more examples of inheritance as we progress through the course. At this point, it is important to understand the basic inheritance that occurs between class and subclass as discussed above. In the Classes & Objects chapter we put the inheritance concepts into practice by defining classes, instantiating objects from those classes, and establishing inheritance relationships.
The second pillar of OO is polymorphism which means 'many forms'. In OO, this characteristic states that methods with the same name can take many forms. Let's consider the fire_weapon() method of the MilitaryAircraft class. The fire_weapon() method could take many forms having multiple versions of its signature. The method signature can have different forms by varying the number and/or type of parameters that the method can receive.
For instance, one version of the fire_weapon() may take one weapon type (e.g. missile) when the method is called. Another version of the same method may take two weapon types (e.g. missile, rocket). Both methods have the name (fire_weapon()) but the different method signatures as determined by argument number and/or type provide polymorphism. The image below shows three versions (forms) of the fire_weapon() method. Python does not support method polymorphism in this way like C++ does. This is primarily due to parameter type determination at runtime and not at compile time like C++. However, in Python, polymorphism is implemented between parent:child classes (in the Classes & Objects chapter). When parent and child both have a function with the same name it is known as overriding which is a form of polymorphism. Python also demonstrates polymorphism like that shown in the runtime polymorphic example below.
As with all concepts in the course, seeing the upcoming code examples will help clarify the abstract concepts. In the following chapters, we will see examples of constructor methods using polymorphism to achieve a variety of behaviors. Constructors are special methods that are called automatically when an object is created (constructed).
In the code example below, runtime polymorphism is introduced. Notice the function go_on_a_trip() accepts a generic object type aircraft. The object type will be determined at runtime based on the type of object that is passed during the call. Notice in the calls to go_on_a_trip(), a MilitaryAircraft object type is passed when the argument is fighter and a CilivilianAircraft object type is passed when the argument is airliner. The appropriate methods are called based on the object type that was passed to go_on_a_trip(). See the results in the output.
The code @staticmethod is called a decorator. It is used to notify Python that the function that follows is a static method. Static methods can be called by either objects or by the class itself. They are covered more thoroughly in the Classes & Objects chapter.
Here's the output.
The third pillar of OO is encapsulation which is the most readily understood. Recall that objects contain two things: functionality and data. Encapsulation is the process and result of including both functionality and data within the object, and in some cases, hiding details from the user. All that the object requires to operate is included within the object itself. There are a number of benefits provided by encapsulation. The first benefit is considered synonymous with encapsulation and that is information hiding. By including data and functionality behind the object walls and controlling what is exposed to the exterior, developers are able to 'hide information'. This makes life better for the developer, for the code, and for the user.
We have heard the expression TMI or too much information. This is applicable in software engineering when too much detail is exposed to the developer or running code. With over exposure, clarity and simplicity are compromised. For example, we fortunately do not need to know the many hundreds of operations and data points required to start a modern automobile. We simply turn the key and the car starts. The voltage, amperage, capacitance, starter metrics, etc. are not 'exposed' to the driver. That information is hidden.
Another benefit provided by encapsulation is compartmentalization or modularity. In OO, programmers have the system of packaging functionality and data together which enhances both class development and code maintenance. During the design process, using encapsulation, the OO designer can more naturally envision and plan object architecture and interactions. And, when the code requires modifications or updates, the modular nature of OO makes it more convenient for the code maintainer.
Security is also enhanced by encapsulation. This is closely related to the concept of information hiding since it induces a 'need to know' requirement. That is, other code within or interacting with the program only has access to the information they need to know. Back to the starting car example. The driver needs to set the correct voltage and amperage sent from the battery to the starter. However, this is performed securely by the turn of key and information hiding. Not only would it be unnecessarily complex to require the driver to set that data directly, it would expose the car to potential damage if the incorrect settings (data) were set by the driver.
See the Resources Chapter for more on Object Orientation.