Object-oriented Programming

Table of contents

No heading

No headings in the article.

What is OOP?

Object-oriented programming is a programming style that constructs software architecture around data or objects rather than functions and logic. An object is a data field with particular characteristics and behaviours. OOP (as programmers popularly abbreviate it) is a programming style aligned or built around objects.

It is crucial to master OOP for each programming language being used because the principles of OOP are handled slightly differently in each language. In this article, Python will be used to explain the concepts of OOP. Other well-known object-oriented programming languages include Objective C, Perl, Python, Javascript, Simula, Modula, Ada and Java.

As earlier stated, object-oriented programming is centred on constructing reusable "objects" with unique features and behaviours that can be interacted with, modified, and packaged. Large, sophisticated, and actively updated or maintained programs fit this development method well.

Classes, Objects, Polymorphism, Abstraction, Encapsulation, and Inheritance are the foundations of OOP.

Classes

Classes are custom datatypes developers create that serve as a blueprint for an object. They consist of data members and member functions, which can only be used by creating an instance of that class. It is the collection of attributes or operations shared by all objects of a particular type. By convention class names adopt the Pascal case naming convention.

Syntax

class ClassName:
    'Optional class documentation string'
    # functions, methods and other implementations go in here

Example

class Person:
    'This is a base class for a person'
    height = 0

    def __init__(self, name, age=18):
        self.name = name
        self.age = age
        Person.height = 170

Objects

An object is an instance of a class; usually, no memory is allocated when a class is created, but memory is allocated when an object is initialized. An object's identity, state, and behaviour are all defined. Each object contains all the data and code needed to manipulate the object.

class Person:
    'This is a base class for a person'
    height = 0

    def __init__(self, name, age=18):
        self.name = name
        self.age = age
        Person.height = 170 

# This line of code will create the person1 object from the 'Person' class and set the name to Harry and age to 33*  
person1 = Person("Harry", 33)


# This line of code will create the person2 object from the 'Person' class and set the name to Meghan and age to 18 (since no age was specified when the object was created, the default age set by the developer will be used)  
person2 = Person("Meghan")

Polymorphism

Polymorphism can be defined as different forms of a method or function. It is helpful when creating other classes that have methods with the same name. Polymorphism makes reusing code easy and also less complex.

Polymorphism can occur in

  • Operators

The + operator can take two inputs and return a result depending on the inputs. In the examples below,

  • The integer inputs produced an integer output

  • The output changed to float because one of the inputs is a float

  • Lastly, strings were concatenated when operated upon

Example

diameter = 6
height = 12
pi = 3.142

print(height + diameter) # 18
print(type(height + diameter)) # <class 'int'>
print(height + pi) # 15.142
print(type(height + pi)) # <class 'float'>

string1 = "Star" 
string2 = "Gazing"

print(string1 + string2) # StarGazing
print(type(string1 + string2)) # <class 'str'>

All this occurs automatically as a result of the way Python's + operator was designed.

  • Built-in functions

We can also see that certain built-in Python functions can accept inputs of various types and then handle them in various ways. Every letter is counted when a string value is passed to len(). However, it behaves differently if a tuple, list or dictionary is provided as input.

Example

string1 = 'Python is amazing!'

tuple1 = ('Spring','Summer','Autumn','Winter')

list1 = ['Africa','Europe','Asia']

dictionary1 = {'Eyes':'Blue','Hair':'Blonde','Piercings':'None', 'Rings': 2}

print(len(string1)) # 18
print(len(tuple1)) # 4
print(len(list1)) # 3
print(len(dictionary1)) # 4
  • User-defined functions

Methods with the same name but wrapped in distinct class names can be created. Therefore, we can repeat running the same method with a different class name prefixed to get different results. Two classes, a window class and a door class, are created and initialized in the example below, with both open and closed methods.

class Window:
    def __init__(self, angle):
        self.angle = angle

    def open(self):
        return print(f"Window opened {self.angle} degrees")

    def close(self):
        return print(f"Window closed {self.angle} degrees")

Encapsulation

Encapsulation is a technique for limiting access to specific areas of an object or class to avoid unintentionally changing data or behaviour. Python uses private and protected instance variables and methods to implement encapsulation. A double underscore is used to indicate private variables and methods, while a single underscore is used to indicate protected variables and methods. Using encapsulation ensures that the internal implementation details of the source code are hidden from the outside world.

The public Access Modifier

The public member can be accessed from either inside or outside the class.

The protected Access Modifier

Only the class and its subclasses provide access to the protected member. An underscore before the member name indicates that the name is protected.

The private Access Modifier

The private member is accessible only inside the class. Adding two underscores before the member name indicates the member is private.

class Employee: 
    def __init__(self, name, department, salary): 
        # public member 
        self.name = name 
        # protected member 
        self._department = department 
        # private member 
        self.__salary = salary 

# Initializing an object with the employee class 
employee = Employee('James Bond', 'MI5', 10000) 

# Attempting to access private data members fails
print('Salary:', employee.__salary)  # AttributeError: 'Employee' object has no attribute '__salary'

# This can be fixed by changing to last line to:
print('Salary:', employee._Employee__salary)

Getters and Setters

Python requires the usage of setters and getters in order to provide good encapsulation. Data encapsulation is the primary goal of using getters and setters in object-oriented applications. To access and edit data members, use the getter and setter methods, respectively.

class Employee:
    def __init__(self, name, id):
        # public member
        self.name = name
        # private member
        self.__id = id

    def show(self):
        print(f'Employee name: {self.name}, \n Employee ID: {self.__id}')

    # getter methods
    def get_id(self):
        return self.__id

    # setter method to modify data member
    def set_id(self, emp_id):
    # conditional state to allow/prevent data modification
        if emp_id == self.__id:
            print('Error, new ID is the same as the old ID')
        else:
            self.__id = emp_id

emp_mark = Employee('Mark', 1075878)

# Before modification
emp_mark.show()
# Changing the ID using setter
emp_mark.set_id(1075373)
# After modification
emp_mark.show()

Abstraction

Abstraction is the practice of shielding the user from the implementation specifics of a class or method. Because of this, the user may concentrate on what the class or function performs instead of worrying about how it is implemented.

Inheritance

Like in real life, inheritance implies creating a new class by deriving from a pre-existing class. The pre-existing class is called the parent class, and the derived class is called the child class. The child class inherits the attributes of the parent class and can be used as if they were defined in the child class. The child class also can override methods from its parent class.

class ChildClass (ParentClass):
    'Optional class documentation string'

    # class methods, functions and other implementations go in here

class Microsoft:

    def __init__(self):
        return print ("Welcome to Microsoft")

    def location(self):
        return print ("Worldwide")

    def industry(self):
        return print ("Technology")

class ADC(Microsoft):
    def __init__(self):
        return print ("Welcome to Microsoft Africa Development Centre")

    # The location method in the child class overwrites the location method in the parent class
    def location(self):
        return print ("Lagos, Nigeria")

    # The industry methods is not defined in the child class so it will be inherited from the parent class

parent_company = Microsoft()    # Welcome to Microsoft
parent_company.location()    # Worldwide
parent_company.industry()    # Technology

child_company = ADC() # Welcome to Microsoft Africa Development Centre
child_company.location()    # Lagos, Nigeria
child_company.industry()    # Technology

Advantages of object-oriented programming

Object-oriented programming is considered more productive than conventional procedure-based programming techniques in terms of software development due to some of the following reasons:

Modularity: Object-oriented programming is modular because it creates a clear division of labour in the creation of object-based programs.

Extensibility: It is also extendable, as new characteristics and actions may be added to objects.

Reusability: Objects can be reused within and between apps. This effectively increases productivity as less time can be spent writing code.

Faster development is made possible via reuse. Rich object libraries are provided with object-oriented programming languages, and project-specific code can be reused in other projects.

High-quality software: Software of higher quality is produced more quickly and at a lesser cost, which frees up more time and resources for the software's verification.

Disadvantages of object-oriented programming

Steep learning curve: Some fundamental programming concepts, like inheritance and polymorphism, can be initially difficult to grasp. It may take some time for some people to get used to how object-oriented programming requires them to think.

Increased program size: Procedural applications often have fewer lines of code than object-oriented programs.

Longer runtime: Object-oriented programs are typically slower than procedure-based programs because they often need to execute more instructions.

Not appropriate for every issue: Using object-oriented programming to solve some problems will not produce efficient programs because some problems are better suited to functional programming, logic programming, or procedure-based programming.

Conclusion

Designing software with OOP concepts can be a task. If there is a clearly outlined plan for a program, OOP can be used to create it. The size of OOP-developed programs is larger than that of procedural-developed applications but is much easier to maintain. OOP is recommended paradigm for most projects as the advantages seemingly outweigh the disadvantages, but when planning a project, it is recommended to explore all available options.