What is the difference between @staticmethod and @classmethod?

What is the difference between a function decorated with&nbsp;<code><a href="http://docs.python.org/library/functions.html#staticmethod" target="_blank">@staticmethod</a></code>&nbsp;and one decorated with&nbsp;<code><a href="http://docs.python.org/library/functions.html#classmethod" target="_blank">@classmethod</a></code>?

What is the difference between a function decorated with @staticmethod and one decorated with @classmethod?

Polymorphism In Java — How To Get Started With OOPs?

Polymorphism In Java — How To Get Started With OOPs?

<strong>This article on Polymorphism in Java talks about one of the key concepts of object oriented programming in depth.</strong>

This article on Polymorphism in Java talks about one of the key concepts of object oriented programming in depth.

In the real world, you might have seen a chameleon changing its color as per its requirement. If someone asks, “How it does that?”, you can simply say, “Because, it is polymorphic”. Similarly, in the programming world, Java objects possess the same functionality where each object can take multiple forms. This property is known as Polymorphism in Java, where Poly means many and morph means change (or ‘form’). In this article, let’s discuss this key concept of Object Oriented Programming i.e. Polymorphism in Java.

Below are the topics to be covered in this article:

  • What is Polymorphism?
  • Polymorphism in Java with Example
  • Types of Polymorphism in Java
  1. Static Polymorphism
  2. Dynamic Polymorphism
  • What is Polymorphism?
  • Polymorphism in Java with Example
  • Types of Polymorphism in Java

You can go through this OOPs concepts video lecture from where you can learn each & every nitty gritties of the technology.

What is Polymorphism?

Polymorphism is the ability of an entity to take several forms. In object-oriented programming, it refers to the ability of an object (or a reference to an object) to take different forms of objects. It allows a common data-gathering message to be sent to each class. Polymorphism encourages called as ‘extendibility’ which means an object or a class can have it’s uses extended.

In the above figure, you can see, Man is only one, but he takes multiple roles like — he is a dad to his child, he is an employee, a salesperson and many more. This is known as Polymorphism.

Now, let’s understand this by taking a real-life example and see how this concept fits into Object-oriented programming.

Polymorphism in Java with Example

Let’s understand this (the example) with the help of below problem statement.

Consider a cell phone where you save your Contacts. Suppose a person has two contact numbers. For the ease of accessibility, your cellphone provides you the functionality where you can save two numbers under the same name.
Similarly, in Java, an object is only one but it can take multiple forms depending on the context of the program. Suppose you want to write a function to save two contact numbers of the same person, you can create it like — void createContact(String name, int number1, int number2).

Now, it’s not necessary that everyone in your contact list will have two contact numbers. Few of them might be having only a single contact number. In such situations, instead of creating another method with a different name to save one number for a contact, what you can do is, create another method with the same name i.e. createContact(). But, instead of taking two contact numbers as parameters, take only one contact number as a parameter i.e. void createContact(String name, int number1).

As you can see in the above figure, createContact() method has two different definitions. Here, which definition is to be executed depends upon the number of parameters being passed. If one parameter is passed, then only a single contact number is saved under the contact. But, if two contact numbers are passed to this method at the same time, then both will be saved under the same contact. This is also known as Method Overloading*.*

Now let’s take another example and understand polymorphism in depth.

Consider a cell phone where you save your Contacts. Suppose a person has two contact numbers. For the ease of accessibility, your cellphone provides you the functionality where you can save two numbers under the same name.
Now relating this concept to an object-oriented language like Java, suppose you have a class named XJeans which includes a method named jeans(). Using this method, you can get an Allen Solly jeans. For the Jeans in the neighboring town, there is another class YJeans. Both the classes XJeans and YJeans extends the parent class ABCShoppingCenter. The YJeans class includes a method named jeans(), using which you can get both the jeans variants.

classABCShoppingCenter {
public void jeans() {
System.out.println("Default AllenSolly Jeans");
}
}
class XJeans extends ABCShoppingCenter {
public void jeans() {
System.out.println("Default AllenSolly Jeans");
}
}
class YJeans extends ABCShoppingCenter { 
// This is overridden method
public void jeans() {
System.out.println("New variant of AllenSolly");
} 
}

So, instead of creating different methods for every new variant, we can have a single method jeans**()**, which can be defined as per the different child classes. Thus, the method named jeans() has two definitions — one with only default jeans and other with both, the default jeans and the new variant. Now, which method gets invoked will depend on the type of object it belongs to. If you create ABCShoppingCenter class object, then there will be only one jeans available. But if you create YJeans class object, that extends ABCShoppingCenter class, then you can have both the variants. This is also known as Method Overriding*.* Thus, Polymorphism increases the simplicity and readability of the code by reducing the complexity. This makes Polymorphism in Java a very useful concept and it can be applied in real-world scenarios as well.

I hope you got an idea on the concept of Polymorphism. Now, let’s move further with this article and understand different types of Polymorphism in Java.

Types of Polymorphism in Java

Java supports two types of polymorphism and they are as follows:

  • What is Polymorphism?
  • Polymorphism in Java with Example
  • Types of Polymorphism in Java

Static Polymorphism

A polymorphism that is resolved during compile time is known as static polymorphism. Method overloading is an example of compile time polymorphism.

Example

Method Overloading is a feature that allows a class to have two or more method to have the same name, but with different parameter lists. In the below example, you have two definitions of the same method add(). So, which add() method would be called is determined by the parameter list at the compile time. That is the reason this is also known as compile time polymorphism.

class Calculator
{
int add(int x, int y)
{
return x+y;
}
int add(int x, int y, int z) 
{
return x+y+z;
}
}
public class Test
{
public static void main(String args[])
{
Calculator obj = new Calculator();
System.out.println(obj.add(100, 200));
System.out.println(obj.add(100, 200, 300));
}
}

This is how Static Polymorphism works. Now, let’s understand what is Dynamic Polymorphism in Java.

Dynamic Polymorphism

Dynamic polymorphism is a process in which a call to an overridden method is resolved at runtime, that’s why it is called runtime polymorphism. Method Overriding is one of the ways to achieve Dynamic Polymorphism. In any object-oriented programming language, Overriding is a feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its super-classes or parent classes.

Example

 In the below example, you have two classes MacBook and iPadMacBook is a parent class and iPad is a child class. The child class is overriding the method myMethod() of the parent class. Here, I have assigned child class object to the parent class reference to determine which method would be called at run-time. It is the type of object that determines which version of the method would be called (not the type of reference).

class MacBook{
public void myMethod(){
System.out.println("Overridden Method");
}
}
public class iPad extends MacBook{
public void myMethod(){
System.out.println("Overriding Method");
}
public static void main(String args[]){
MacBook obj = new iPad();
obj.myMethod();
}
}

Output:

Overriding Method

When you invoke the overriding method, then the object determines which method is to be executed. Thus, this decision is made at the run time.

I have listed down few more overriding examples.

MacBook obj = new MacBook();
obj.myMethod();
// This would call the myMethod() of parent class MacBook

iPad obj = new iPad();
obj.myMethod();
// This would call the myMethod() of child class iPad

MacBook obj = new iPad();
obj.myMethod();
// This would call the myMethod() of child class iPad

In the third example, the method of the child class is to be executed because the method that needs to be executed is determined by the type of object. Since the object belongs to the child class, the child class version of myMethod() is called.

Advantages of Dynamic Polymorphism

  1. Static Polymorphism
  2. Dynamic Polymorphism

This was all about different types. Now let’s see some important other characteristics of Polymorphism.

Other Characteristics of Polymorphism in Java

In addition to these two main types of polymorphism in Java, there are other characteristics in the Java programming language that exhibits polymorphism like:

  • What is Polymorphism?
  • Polymorphism in Java with Example
  • Types of Polymorphism in Java

Let’s discuss some of these characteristics.

Coercion

Polymorphic coercion deals with implicit type conversion done by the compiler to prevent type errors. A typical example is seen in an integer and string concatenation.

String str="string"=2;

Operator Overloading

An operator or method overloading refers to a polymorphic characteristic of same symbol or operator having different meanings (forms) depending on the context. For example, the plus symbol (+) is used for mathematical addition as well as String concatenation. In either case, only context (i.e. argument types) determines the interpretation of the symbol.

String str = "2" + 2;
int sum = 2 + 2;
System.out.println(" str = %s\n sum = %d\n", str, sum);

Output:

str = 22sum = 4

Polymorphic Parameters

Parametric polymorphism allows a name of a parameter or method in a class to be associated with different types. In the below example I have defined content as a String and later as an Integer:

public class TextFile extends GenericFile{
private String content;
public String setContentDelimiter(){
int content = 100;
this.content = this.content + content;
}
}

Note: Declaration of polymorphic parameters can lead to a problem known as variable hiding**.**

Here, the local declaration of a parameter always overrides the global declaration of another parameter with the same name. To solve this problem, it is often advisable to use global references such as this keyword to point to global variables within a local context.

With this, we come to an end on Polymorphism in Java article. Do look out for other articles and videos in the series which will help you understand various concepts of Java.

If you wish to check out more articles on the market’s most trending technologies like Artificial Intelligence, DevOps, Ethical Hacking, then you can refer to Edureka’s official site.

Python vs Java: Understand Object Oriented Programming

Python vs Java: Understand Object Oriented Programming

This article compares and contrasts object-oriented programming support in Python vs Java. By the end, you’ll be able to apply your knowledge of object-oriented programming to Python, understand how to reinterpret your understanding of Java objects to Python, and use objects in a Pythonic way.

Java programmers making a move to Python often struggle with Python’s approach to object-oriented programming (OOP). The approach to working with objects, variable types, and other language capabilities taken by Python vs Java are quite different. It can make switching between both languages very confusing.

Over the course of this article, you’ll:

  • Build a basic class in both Java and Python
  • Explore how object attributes work in Python vs Java
  • Compare and contrast Java methods and Python functions
  • Discover inheritance and polymorphism mechanisms in both languages
  • Investigate reflection across Python vs Java
  • Apply everything in a complete class implementation in both languages

This article isn’t a primer on object-oriented programming. Rather, it compares object-oriented features and principles of Python vs Java. Readers should have good knowledge of Java, and also be familiar with coding Python.

Table of Contents

  • Sample Classes in Python vs Java
  • Object Attributes
    Declaration and InitializationPublic and PrivateAccess Controlself and this* Methods and Functions
  • Inheritance and Polymorphism
    InheritanceTypes and PolymorphismDefault MethodsOperator Overloading* Reflection
    Examining an Object’s TypeExamining an Object’s AttributesCalling Methods Through Reflection* Conclusion
Sample Classes in Python vs Java

To begin, you’ll implement the same small class in both Python and Java to illustrate the differences between them. You’ll make modifications to them as the article progresses.

First, assume you have the following Car class definition in Java:

public class Car {
    private String color;
    private String model;
    private int year;
    public Car(String color, String model, int year) {
        this.color = color;
        this.model = model;
        this.year = year;
    }
    public String getColor() {
        return color;
    }
    public String getModel() {
        return model;
    }
    public int getYear() {
        return year;
    }
}

Java classes are defined in files with the same name as the class. So, you have to save this class in a file named Car.java. Only one class can be defined in each file.

A similar small Car class is written in Python as follows:

class Car:
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year

In Python you can declare a class anywhere, in any file, at any time. Save this class in the file car.py.

Using these classes as your base, you can explore the basic components of classes and objects.

Object Attributes

All object oriented languages have some way to store data about the object. In **Java **and Python, data is stored in attributes, which are variables associated with specific objects.

One of the most significant differences between Python vs Java is how they define and manage class and object attributes. Some of these differences come from constraints imposed by the languages, while others come from best practices.

Declaration and Initialization

In Java, you declare attributes in the class body, outside of any methods, with a definite type. You must define class attributes before they are used:

public class Car {
    private String color;
    private String model;
    private int year;

In Python, you both declare and define attributes inside the class <strong>init</strong>(), which is the equivalent of Java’s constructor:

def __init__(self, color, model, year):
    self.color = color
    self.model = model
    self.year = year

By prefixing the variable names with self, you tell Python these are attributes. Each instance of the class will have a copy. All variables in Python are loosely typed, and these attributes are no exception.

You can also create instance variables outside of .<strong>init</strong>(), but it’s not a best practice as their scope is often confusing. If not used properly, instance variables created outside of .__init__() can lead to subtle bugs that are hard to find. For example, you can add a new attribute .wheels to a Car object like this:

>>> import car
>>> my_car = car.Car("yellow", "beetle", 1967)
>>> print(f"My car is {my_car.color}")
My car is yellow
>>> my_car.wheels = 5
>>> print(f"Wheels: {my_car.wheels}")
Wheels: 5

However, if you forget the my_car.wheels = 5 on line 6, then Python displays an error:

>>> import car
>>> my_car = car.Car("yellow", "beetle", 1967)
>>> print(f"My car is {my_car.color}")
My car is yellow
>>> print(f"Wheels: {my_car.wheels}")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Car' object has no attribute 'wheels'

In Python, when you declare a variable outside of a method, it’s treated as a class variable. Update the Car class as follows:

class Car:
    wheels = 0
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year

This changes how you use the variable wheels. Instead of referring to it using an object, you refer to it using the class name:

>>> import car
>>> my_car = car.Car("yellow", "beetle", 1967)
>>> print(f"My car is {my_car.color}")
My car is yellow
>>> print(f"It has {car.Car.wheels} wheels")
It has 0 wheels
>>> print(f"It has {my_car.wheels} wheels")
It has 0 wheels

Note: In Python, you refer to a class variable using the following syntax:

  1. The name of the file containing the class, without the .py extension
  2. A dot
  3. The name of the class
  4. A dot
  5. The name of the variable

Since you saved the Car class in the file car.py, you refer to the class variable wheels on line 6 as car.Car.wheels.

You can refer to my<em>car.wheels or car.Car.wheels, but be careful. Changing the value of the instance variable my</em>car.wheels will not change the value of the class variable car.Car.wheels:

>>> from car import *
>>> my_car = car.Car("yellow", "Beetle", "1966")
>>> my_other_car = car.Car("red", "corvette", "1999")
>>> print(f"My car is {my_car.color}")
My car is yellow
>>> print(f"It has {my_car.wheels} wheels")
It has 0 wheels
>>> print(f"My other car is {my_other_car.color}")
My other car is red
>>> print(f"It has {my_other_car.wheels} wheels")
It has 0 wheels
>>> # Change the class variable value
... car.Car.wheels = 4
>>> print(f"My car has {my_car.wheels} wheels")
My car has 4 wheels
>>> print(f"My other car has {my_other_car.wheels} wheels")
My other car has 4 wheels
>>> # Change the instance variable value for my_car
... my_car.wheels = 5
>>> print(f"My car has {my_car.wheels} wheels")
My car has 5 wheels
>>> print(f"My other car has {my_other_car.wheels} wheels")
My other car has 4 wheels

You define two Car objects on lines 2 and 3:

  1. my_car
  2. my_other_car

At first, both of them have zero wheels. When you set the class variable using car.Car.wheels = 4 on line 16, both objects now have four wheels. However, when you set the instance variable using my_car.wheels = 5 on line 24, only that object is affected.

This means that there are now two different copies of the wheels attribute:

  1. A class variable that applies to all Car objects
  2. A specific instance variable applicable to the my_car object only

It isn’t difficult to accidentally refer to the wrong one and introduce subtle bugs.

Java’s equivalent to a class attribute is a static attribute:

public class Car {
    private String color;
    private String model;
    private int year;
    private static int wheels;

    public Car(String color, String model, int year) {
        this.color = color;
        this.model = model;
        this.year = year;
    }

    public static int getWheels() {
        return wheels;
    }

    public static void setWheels(int count) {
        wheels = count;
    }
}

Normally, you refer to static variables using the Java class name. You can refer to a static variable through a class instance like Python, but it’s not a best practice.

Your Java class is getting long. One of the reasons why Java is more verbose than Python is the notion of public and private methods and attributes.

Public and Private

Java controls access to methods and attributes by differentiating between public data and private data.

In Java, it is expected that attributes are declared as private, or protected if subclasses need direct access to them. This limits access to these attributes from code outside the class. To provide access to private attributes, you declare public methods which set and retrieve data in a controlled manner (more on that later).

Recall from your Java class above that the color variable was declared as private. Therefore, this Java code will show a compilation error at the highlighted line:

Car myCar = new Car("blue", "Ford", 1972);

// Paint the car
myCar.color = "red";

If you don’t specify an access level, then the attribute defaults to package protected, which limits access to classes in the same package. You have to mark the attribute as public if you want this code to work.

However, declaring public attributes is not considered a best practice in Java. You’re expected to declare attributes as private, and use public access methods, such as the .getColor() and .getModel() shown in the code.

Python doesn’t have the same notion of private or protected data that Java does. Everything in Python is public. This code works with your existing Python class just fine:

>>> my_car = car.Car("blue", "Ford", 1972)

>>> # Paint the car
... my_car.color = "red"

Instead of private, Python has a notion of a non-public instance variable. Any variable which starts with an underscore character is defined to be non-public. This naming convention makes it harder to access a variable, but it’s only a naming convention, and you can still access the variable directly.

Add the following line to your Python Car class:

class Car:

    wheels = 0

    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self._cupholders = 6

You can access the ._cupholders variable directly:

>>> import car
>>> my_car = car.Car("yellow", "Beetle", "1969")
>>> print(f"It was built in {my_car.year}")
It was built in 1969
>>> my_car.year = 1966
>>> print(f"It was built in {my_car.year}")
It was built in 1966
>>> print(f"It has {my_car._cupholders} cupholders.")
It has 6 cupholders.

Python lets you access ._cupholders, but IDEs such as VS Code may issue a warning through linters that support PEP 8.

Here’s the code in VS Code, with a warning highlighted and displayed:

Python further recognizes using double underscore characters in front of a variable to conceal an attribute in Python. When Python sees a double underscore variable, it changes the variable name internally to make it difficult to access directly. This mechanism avoids accidents but still doesn’t make data impossible to access.

To show this mechanism in action, change the Python Car class again:

class Car:

    wheels = 0

    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self.__cupholders = 6

Now, when you try to access the .__cupholders variable, you see the following error:

>>> import car
>>> my_car = car.Car("yellow", "Beetle", "1969")
>>> print(f"It was built in {my_car.year}")
It was built in 1969
>>> my_car.year = 1966
>>> print(f"It was built in {my_car.year}")
It was built in 1966
>>> print(f"It has {my_car.__cupholders} cupholders.")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Car' object has no attribute '__cupholders'

So why doesn’t the .__cupholders attribute exist?

When Python sees an attribute with double underscores, it changes the attribute by prefixing the original name of the attribute with an underscore, followed by the class name. To use the attribute directly, you need to change the name you use as well:

>>> print(f"It has {my_car._Car__cupholders} cupholders")
It has 6 cupholders

When you use double underscores to conceal an attribute from the user, Python changes the name in a well-documented manner. This means that a determined developer can still access the attribute directly.

So if your Java attributes are declared private, and your Python attributes are prefaced with double underscores, then how do you provide and control access to the data they store?

Access Control

In Java, you access private attributes using setters and getters. To allow users to paint their cars, add the following code to your Java class:

public String getColor() {
    return color;
}

public void setColor(String color) {
    this.color = color;
}

Since .getColor() and .setColor() are public, anyone can call them to change or retrieve the car’s color. Java’s best practices of using private attributes accessed with public getters and setters is one of the reasons why Java code tends to be more verbose than Python.

As you saw above, you access attributes directly in Python. Since everything is public, you can access anything at any time from anywhere. You set and get attribute values directly by referring to their names. You can even delete attributes in Python, which isn’t possible in Java:

>>> my_car = Car("yellow", "beetle", 1969)
>>> print(f"My car was built in {my_car.year}")
My car was built in 1969
>>> my_car.year = 1966
>>> print(f"It was built in {my_car.year}")
It was built in 1966
>>> del my_car.year
>>> print(f"It was built in {my_car.year}")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Car' object has no attribute 'year'

However, there are times you may want to control access to an attribute. In that case, you can use Python properties.

In Python, properties provide controllable access to class attributes using Python decorator syntax. Properties allow functions to be declared in Python classes that are analogous to Java getter and setter methods, with the added bonus of allowing you to delete attributes as well.

You can see how properties work by adding one to your Car class:

class Car:
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self._voltage = 12
    @property
    def voltage(self):
        return self._voltage

    @voltage.setter
    def voltage(self, volts):
        print("Warning: this can cause problems!")
        self._voltage = volts

    @voltage.deleter
    def voltage(self):
        print("Warning: the radio will stop working!")
        del self._voltage

Here, you expand the notion of the Car to include electric vehicles. You declare the ._voltage attribute to hold the battery voltage on line 6.

To provide controlled access, you define a function called voltage() to return the private value on lines 9 and 10. By using the @property decoration, you mark it as a getter that anyone can access directly.

Similarly, you define a setter function on lines 13 to 15, also called voltage(). However, you decorate this function with @voltage.setter. Lastly, you use @voltage.deleter to decorate a third voltage() on lines 18 to 20, which allows controlled deletion of the attribute.

The names of the decorated functions are all the same, indicating they control access to the same attribute. The function names also become the name of the attribute you use to access the value. Here’s how these properties work in practice:

>>> from car import *
>>> my_car = Car("yellow", "beetle", 1969)
>>> print(f"My car uses {my_car.voltage} volts")
My car uses 12 volts
>>> my_car.voltage = 6
Warning: this can cause problems!
>>> print(f"My car now uses {my_car.voltage} volts")
My car now uses 6 volts
>>> del my_car.voltage
Warning: the radio will stop working!

Note that you use .voltage in the highlighted lines above, not ._voltage. This tells Python to use the property functions you defined:

  • When you print the value of my_car.voltage on line 4, Python calls .voltage() decorated with @property.
  • When you assign a value to my_car.voltage on line 7, Python calls .voltage() decorated with @voltage.setter.
  • When you delete my_car.voltage on line 13, Python calls .voltage() decorated with @voltage.deleter.

The @property, @.setter, and @.deleter decorations make it possible to control access to attributes without requiring users to use different methods. You can even make attributes appear to be read-only properties by omitting the @.setter and @.deleter decorated functions.

self and this

In Java, a class refers to itself with the this reference:

public void setColor(String color) {
    this.color = color;
}

this is implicit in Java code: it doesn’t normally need to be written, unless there may be confusion between two variables with the same name.

You can write the same setter this way:

public void setColor(String newColor) {
    color = newColor;
}

Since Car has an attribute named .color, and there isn’t another variable in scope with the same name, a reference to that name works. We used this in the first example to differentiate between the attribute and parameter both named color.

In Python, the keyword self serves a similar purpose. It’s how you refer to member variables, but unlike Java’s this, it’s required if you want to create or refer to a member attribute:

class Car:
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
        self._voltage = 12

    @property
    def voltage(self):
        return self._voltage

Python requires each self in the code above. Each one either creates or refers to the attributes. If you omit them, then Python will create a local variable instead of an attribute.

The difference between how you use self and this in Python and Java is due to underlying differences between the two languages and how they name variables and attributes.

Methods and Functions

This difference between Python vs Java is, simply put, that Python has functions, and Java doesn’t.

In Python, the following code is perfectly fine (and very common):

>>> def say_hi():
...     print("Hi!")
... 
>>> say_hi()
Hi!

You can call say_hi() from anywhere it’s visible. This function has no reference to self, which indicates that it’s a global function, not a class function. It can’t alter or store any data in any classes but can use local and global variables.

In contrast, every line of Java code you write belongs to a class. Functions can’t exist outside of a class, and by definition, all Java functions are methods. In Java, the closest you can get to a pure function is by using a static method:

public class Utils {
    static void SayHi() {
        System.out.println("Hi!");
    }
}

Utils.SayHi() is callable from anywhere without first creating an instance of Utils. Since you can call SayHi() without creating an object first, the this reference doesn’t exist. However, this is still not a function in the sense that say_hi() is in Python.

Inheritance and Polymorphism

Inheritance and polymorphism are two fundamental concepts in object-oriented programming.

Inheritance allows objects to derive attributes and functionality from other objects, creating a hierarchy moving from more general objects to more specific. For example, a Car and a Boat are both specific types of Vehicles. Objects can inherit their behavior from a single parent object or multiple parent objects, and are referred to as child objects when they do.

Polymorphism allows two or more objects to behave like one another, which allows them to be used interchangeably. For example, if a method or function knows how to paint a Vehicle object, then it can also paint a Car or Boat object, since they inherit their data and behavior from the Vehicle.

These fundamental OOP concepts are implemented quite differently in Python vs Java.

Inheritance

Python supports multiple inheritance, or creating classes that inherit behavior from more than one parent class.

To see how this works, update the Car class by breaking it into two categories, one for vehicles, and one for devices that use electricity:

class Vehicle:
    def __init__(self, color, model):
        self.color = color
        self.model = model

class Device:
    def __init__(self):
        self._voltage = 12

class Car(Vehicle, Device):
    def __init__(self, color, model, year):
        Vehicle.__init__(self, color, model)
        Device.__init__(self)
        self.year = year

    @property
    def voltage(self):
        return self._voltage

    @voltage.setter
    def voltage(self, volts):
        print("Warning: this can cause problems!")
        self._voltage = volts

    @voltage.deleter
    def voltage(self):
        print("Warning: the radio will stop working!")
        del self._voltage

A Vehicle is defined as having .color and .model attributes. Then, a Device is defined to have a ._voltage attribute. Since the original Car object had these three attributes, it can be redefined to inherit both the Vehicle and Device classes. The color, model, and _voltage attributes will be part of the new Car class.

In the .__init__() for Car, you call the .__init__() methods for both of the parent classes to make sure everything is initialized properly. Once done, you can add any other functionality you want to your Car. In this case, you add a .year attribute specific to Car objects, and getter and setter methods for .voltage.

Functionally, the new Car class behaves as it always has. You create and use Car objects just as before:

>>> from car import *
>>> my_car = Car("yellow", "beetle", 1969)

>>> print(f"My car is {my_car.color}")
My car is yellow

>>> print(f"My car uses {my_car.voltage} volts")
My car uses 12 volts

>>> my_car.voltage = 6
Warning: this can cause problems!

>>> print(f"My car now uses {my_car.voltage} volts")
My car now uses 6 volts

Java, on the other hand, only supports single inheritance, which means classes in Java can inherit data and behavior from only a single parent class. However, Java objects can inherit behavior from many different interfaces. Interfaces provide a group of related methods an object must implement, and allow multiple child classes to behave similarly.

To see this in action, split the Java Car class into a parent class and an interface:

public class Vehicle {

    private String color;
    private String model;

    public Vehicle(String color, String model) {
        this.color = color;
        this.model = model;
    }

    public String getColor() {
        return color;
    }

    public String getModel() {
        return model;
    }
}

public interface Device {
    int getVoltage();
}

public class Car extends Vehicle implements Device {

    private int voltage;
    private int year;

    public Car(String color, String model, int year) {
        super(color, model);
        this.year = year;
        this.voltage = 12;
    }

    @Override
    public int getVoltage() {
        return voltage;
    }

    public int getYear() {
        return year;
    }
}

Remember that each class and interface needs to live in its own file.

As you did with Python, you create a new class called Vehicle to hold the more general vehicle related data and functionality. However, to add the Device functionality, you need to create an interface instead. This interface defines a single method to return the voltage of the Device.

Redefining the Car class requires you to inherit from Vehicle using extend, and implement the Device interface using implements. In the constructor, you call the parent class constructor using the built-in super(). Since there is only one parent class, it can only refer to the Vehicle constructor. To implement the interface, you write getVoltage() using the @Override annotation.

Rather than getting code reuse from Device as Python did, Java requires you to implement the same functionality in every class that implements the interface. Interfaces only define the methods—they cannot define instance data or implementation details.

So why is this the case for Java? It all comes down to types.

Types and Polymorphism

Java’s strict type checking is what drives its interface design.

Every class and interface in Java is a type. Therefore, if two Java objects implement the same interface, then they are considered to be the same type with respect to that interface. This mechanism allows different classes to be used interchangeably, which is the definition of polymorphism.

You can implement device charging for your Java objects by creating a .charge() that takes a Device to charge. Any object that implements the Device interface can be passed to .charge(). This also means that classes that do not implement Device will generate a compilation error.

Create the following class in a file called Rhino.java:

public class Rhino {
}

Now you can create a new Main.java to implement .charge() and explore how Car and Rhino objects differ:

public class Main{
    public static void charge(Device device) {
       device.getVoltage();
    }

    public static void main(String[] args) throws Exception {
        Car car = new Car("yellow", "beetle", 1969);
        Rhino rhino = new Rhino();
        charge(car);
        charge(rhino);
    }
}

Here is what you should see when you try to build this code:

Information:2019-02-02 15:20 - Compilation completed with 
    1 error and 0 warnings in 4 s 395 ms
Main.java
Error:(43, 11) java: incompatible types: Rhino cannot be converted to Device

Since the Rhino class doesn’t implement the Device interface, it can’t be passed into .charge().

In contrast to Java’s strict variable typing, Python uses a concept called duck typing, which in basic terms means that if a variable “walks like a duck and quacks like a duck, then it’s a duck.” Instead of identifying objects by type, Python examines their behavior.

You can explore duck typing by implementing similar device charging capabilities for your Python Device class:

>>> def charge(device):
...     if hasattr(device, '_voltage'):
...         print(f"Charging a {device._voltage} volt device")
...     else:
...         print(f"I can't charge a {device.__class__.__name__}")
... 
>>> class Phone(Device):
...     pass
... 
>>> class Rhino:
...     pass
... 
>>> my_car = Car("yellow", "Beetle", "1966")
>>> my_phone = Phone()
>>> my_rhino = Rhino()

>>> charge(my_car)
Charging a 12 volt device
>>> charge(my_phone)
Charging a 12 volt device
>>> charge(my_rhino)
I can't charge a Rhino

charge() must check for the existence of the ._voltage attribute in the object it’s passed. Since the Device class defines this attribute, any class that inherits from it (such as Car and Phone) will have this attribute, and will therefore show they are charging properly. Classes that do not inherit from Device (like Rhino) may not have this attribute, and will not be able to charge (which is good, since charging rhinos can be dangerous).

Default Methods

All Java classes descend from the Object class, which contains a set of methods every other class inherits. Subclasses can either override them or keep the defaults. The Object class defines the following methods:

class Object {
    boolean equals(Object obj) { ... }    
    int hashCode() { ... }    
    String toString() { ... }    
}

By default, <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-" target="_blank">equals()</a> compares the addresses of the current Object with a second Object passed in, and <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--" target="_blank">hashcode()</a> computes a unique identifier that also uses the address of the current Object. These methods are used in many different contexts in Java. For example, utility classes, such as collections that sort objects based on value, need both of them.

<a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#toString--" target="_blank">toString()</a> returns a String representation of the Object. By default, this is the name of the class and the address. This method is called automatically when an Object is passed to a method that requires a String argument, such as System.out.println():

Car car = new Car("yellow", "Beetle", 1969);
System.out.println(car);

Running this code will use the default .toString() to show the car object:

[email protected]

Not very useful, right? You can improve this by overriding the default .toString(). Add this method to your Java Car class:

public String toString() {
    return "Car: " + getColor() + " : " + getModel() + " : " + getYear();
}

Now, when you run the same sample code, you’ll see the following:

Car: yellow : Beetle : 1969

Python provides similar functionality with a set of common dunder (short for “double underscore”) methods. Every Python class inherits these methods, and you can override them to modify their behavior.

For string representations of an object, Python provides __repr__() and __str__(), which you can learn about in Pythonic OOP String Conversion: __repr__ vs __str__. The unambiguous representation of an object is returned by __repr__(), while __str__() returns a human readable representation. These are roughly analogous to .hashcode() and .toString() in Java.

Like Java, Python provides default implementations of these dunder methods:

>>> my_car = Car("yellow", "Beetle", "1966")

>>> print(repr(my_car))
<car.Car object at 0x7fe4ca154f98>
>>> print(str(my_car))
<car.Car object at 0x7fe4ca154f98>

You can improve this output by overriding .__str__(), adding this to your Python Car class:

def __str__(self):
    return f'Car {self.color} : {self.model} : {self.year}'

This gives you a much nicer result:

>>> my_car = Car("yellow", "Beetle", "1966")

>>> print(repr(my_car))
<car.Car object at 0x7f09e9a7b630>
>>> print(str(my_car))
Car yellow : Beetle : 1966

Overriding the dunder method gave us a more readable representation of your Car. You may want to override the .__repr__() as well, as it is often useful for debugging.

Python offers a lot more dunder methods. Using dunder methods, you can define your object’s behavior during iteration, comparison, addition, or making an object callable directly, among other things.

Operator Overloading

Operator overloading refers to redefining how Python operators work when operating on user-defined objects. Python’s dunder methods allow you to implement operator overloading, something that Java doesn’t offer at all.

Modify your Python Car class with the following additional dunder methods:

class Car:
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year

    def __str__(self):
        return f'Car {self.color} : {self.model} : {self.year}'

    def __eq__(self, other):
        return self.year == other.year

    def __lt__(self, other):
        return self.year < other.year

    def __add__(self, other):
        return Car(self.color + other.color, 
                   self.model + other.model, 
                   int(self.year) + int(other.year))

The table below shows the relationship between these dunder methods and the Python operators they represent:

When Python sees an expression containing objects, it calls any dunder methods defined that correspond to operators in the expression. The code below uses these new overloaded arithmetic operators on a couple of Car objects:

>>> my_car = Car("yellow", "Beetle", "1966")
>>> your_car = Car("red", "Corvette", "1967")

>>> print (my_car < your_car)
True
>>> print (my_car > your_car)
False
>>> print (my_car == your_car)
False
>>> print (my_car + your_car)
Car yellowred : BeetleCorvette : 3933 

There are many more operators you can overload using dunder methods. They offer a way to enrich your object’s behavior in a way that Java’s common base class default methods don’t.

Reflection

Reflection refers to examining an object or class from within the object or class. Both Java and Python offer ways to explore and examine the attributes and methods in a class.

Examining an Object’s Type

Both languages have ways to test or check an object’s type.

In Python, you use type() to display the type of a variable, and isinstance() to determine if a given variable is an instance or child of a specific class:

>>> my_car = Car("yellow", "Beetle", "1966")

>>> print(type(my_car))
<class 'car.Car'>
>>> print(isinstance(my_car, Car))
True
>>> print(isinstance(my_car, Device))
True

In Java, you query the object for its type using .getClass(), and use the instanceof operator to check for a specific class:

Car car = new Car("yellow", "beetle", 1969);

System.out.println(car.getClass());
System.out.println(car instanceof Car);

This code outputs the following:

class com.realpython.Car
true

Examining an Object’s Attributes

In Python, you can view every attribute and function contained in any object (including all the dunder methods) using dir(). To get the specific details of a given attribute or function, use getattr():

>>> print(dir(my_car))
['_Car__cupholders', '__add__', '__class__', '__delattr__', '__dict__', 
 '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
 '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
 '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
 '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
 '_voltage', 'color', 'model', 'voltage', 'wheels', 'year']

>>> print(getattr(my_car, "__format__"))
<built-in method __format__ of Car object at 0x7fb4c10f5438>

Java has similar capabilities, but the language’s access control and type safety make it more complicated to retrieve.

.getFields() retrieves a list of all publicly accessible attributes. However, since none of the attributes of Car are public, this code returns an empty array:

Field[] fields = car.getClass().getFields();

Java treats attributes and methods as separate entities, so public methods are retrieved using .getDeclaredMethods(). Since public attributes will have a corresponding .get method, one way to discover if a class contains a specific property might look like this:

  • Use .getFields() to generate an array of all the methods.
  • Loop through all the methods returned:
  • For each method discovered, return true if the method:
    Begins with the word get OR accepts zero argumentsAND doesn’t return voidAND includes the name of the propertyOtherwise, return false.
    Here’s a quick-and-dirty example:
public static boolean getProperty(String name, Object object) throws Exception {
    Method[] declaredMethods = object.getClass().getDeclaredMethods();
    for (Method method : declaredMethods) {
        if (isGetter(method) && 
            method.getName().toUpperCase().contains(name.toUpperCase())) {
              return true;
        }
    }
    return false;
}
// Helper function to get if the method is a getter method
public static boolean isGetter(Method method) {
    if ((method.getName().startsWith("get") || 
         method.getParameterCount() == 0 ) && 
        !method.getReturnType().equals(void.class)) {
          return true;
    }
    return false;
}

getProperty() is your entry point. Call this with the name of an attribute and an object. It returns true if the property is found, and false if not.

Calling Methods Through Reflection

Both Java and Python provide mechanisms to call methods through reflection.

In the Java example above, instead of simply returning true if the property was found, you could call the method directly. Recall that getDeclaredMethods() returns an array of Method objects. The Method object itself has a method called .invoke(), which will call the Method. Instead of returning true when the correct method is found on line 7 above, you can return method.invoke(object) instead.

This capability exists in Python as well. However, since Python doesn’t differentiate between functions and attributes, you have to look specifically for entries that are callable:

>>> for method_name in dir(my_car):
...     if callable(getattr(my_car, method_name)):
...         print(method_name)
... 
__add__
__class__
__delattr__
__dir__
__eq__
__format__
__ge__
__getattribute__
__gt__
__init__
__init_subclass__
__le__
__lt__
__ne__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__

Python methods are simpler to manage and call than in Java. Adding the () operator (and any required arguments) is all you need to do.

The code below will find an object’s .__str__() and call it through reflection:

>>> for method_name in dir(my_car):
...     attr = getattr(my_car, method_name)
...     if callable(attr):
...         if method_name == '__str__':
...             print(attr())
... 
Car yellow : Beetle : 1966

Here, every attribute returned by dir() is checked. You get the actual attribute object using getattr(), and check if it’s a callable function using callable(). If so, you then check if its name is __str__(), and then call it.

Conclusion

Throughout the course of this article, you learned how object-oriented principles differ in Python vs Java. As you read, you:

  • Built a basic class in both Java and Python
  • Explored how object attributes work in Python vs Java
  • Compared and contrasted Java methods and Python functions
  • Discovered inheritance and polymorphism mechanisms in both languages
  • Investigated reflection across Python vs Java
  • Applied everything in a complete class implementation in both languages

Understanding the differences in Python vs Java when handling objects, and the syntax choices each language makes, will help you apply best practices and make your next project smoother.

Object-Oriented Programming — The Trillion Dollar Disaster

Object-Oriented Programming — The Trillion Dollar Disaster

Why it’s time to move on from OOP

Originally published by Ilya Suzdalnitski at https://medium.com

OOP is considered by many to be the crown jewel of computer science. The ultimate solution to code organization. The end to all our problems. The only true way to write our programs. Bestowed upon us by the one true God of programming himself…

Until…it’s not, and people start succumbing under the weight of abstractions, and the complex graph of promiscuously shared mutable objects. Precious time and brainpower are being spent thinking about “abstractions” and “design patterns” instead of solving real-world problems.

Many people have criticized Object-Oriented Programming, including very prominent software engineers. Heck, even the inventor of OOP himself is a well-known critic of modern OOP!

The ultimate goal of every software developer should be to write reliable code. Nothing else matters if the code is buggy and unreliable. And what is the best way to write code that is reliable? Simplicity. Simplicity is the opposite of complexity. Therefore our first and foremost responsibility as software developers should be to reduce code complexity.

Disclaimer

I’ll be honest, I’m not a raving fan of object-orientation. Of course, this article is going to be biased. However, I have good reasons to dislike OOP.

I also understand that criticism of OOP is a very sensitive topic — I will probably offend many readers. However, I’m doing what I think is right. My goal is not to offend, but to raise awareness of the issues that OOP introduces.

I’m not criticizing Alan Kay’s OOP — he is a genius. I wish OOP was implemented the way he designed it. I’m criticizing the modern Java/C# approach to OOP.

I think that it is not right that OOP is considered the de-facto standard for code organization by many people, including those in very senior technical positions. It is also unacceptable that many mainstream languages don’t offer any other alternatives to code organization other than OOP.

Hell, I used to struggle a lot myself while working on OOP projects. And I had no single clue why I was struggling this much. Maybe I wasn’t good enough? I had to learn a couple more design patterns (I thought)! Eventually, I got completely burned out.

This post sums up my first-hand decade-long journey from Object-Oriented to Functional programming. Unfortunately, no matter how hard I try, I can no longer find use cases for OOP. I have personally seen OOP projects fail because they become too complex to maintain.

TLDR

Object oriented programs are offered as alternatives to correct ones…
Edsger W. Dijkstra, pioneer of computer science

Photo by Sebastian Herrmann on Unsplash

Object-Oriented Programming has been created with one goal in mind — to manage the complexity of procedural codebases. In other words, it was supposed to improve code organization. There’s no objective and open evidence that OOP is better than plain procedural programming.

The bitter truth is that OOP fails at the only task it was intended to address. It looks good on paper — we have clean hierarchies of animals, dogs, humans, etc. However, it falls flat once the complexity of the application starts increasing. Instead of reducing complexity, it encourages promiscuous sharing of mutable state and introduces additional complexity with its numerous design patterns. OOP makes common development practices, like refactoring and testing, needlessly hard.

Some might disagree with me, but the truth is that modern Java/C# OOP has never been properly designed. It never came out of a proper research institution (in contrast with Haskell/FP). Lambda calculus offers a complete theoretical foundation for Functional Programming. OOP has nothing to match that.

Using OOP is seemingly innocent in the short-term, especially on greenfield projects. But what are the long-term consequences of using OOP? OOP is a time bomb, set to explode sometime in the future when the codebase gets big enough.

Projects get delayed, deadlines get missed, developers get burned-out, adding in new features becomes next to impossible. The organization labels the codebase as the “legacy codebase”, and the development team plans a rewrite.

OOP is not natural for the human brain, our thought process is centered around “doing” things — go for a walk, talk to a friend, eat pizza. Our brains have evolved to do things, not to organize the world into complex hierarchies of abstract objects.

OOP code is non-deterministic — unlike with functional programming, we’re not guaranteed to get the same output given the same inputs. This makes reasoning about the program very hard. As an oversimplified example, the output of 2+2 or calculator.Add(2, 2) mostly is equal to four, but sometimes it might become equal to three, five, and maybe even 1004. The dependencies of the Calculator object might change the result of the computation in subtle, but profound ways. OOPs…

The Need for a Resilient Framework

Good programmers write good code, bad programmers write bad code, no matter the programming paradigm. However, the programming paradigm should constrain bad programmers from doing too much damage. Of course, this is not you, since you already are reading this article and putting in the effort to learn. Bad programmers never have the time to learn, they only press random buttons on the keyboard like crazy. Whether you like it or not, you will be working with bad programmers, some of them will be really really bad. And, unfortunately, OOP does not have enough constraints in place that would prevent bad programmers from doing too much damage. OOPs…

I don’t consider myself a bad programmer, but even I am unable to write good code without a strong framework to base my work on. Yes, there are frameworks that concern themselves with some very particular problems (e.g. Angular or ASP.Net).

I’m not talking about the software frameworks. I’m talking about the more abstract dictionary definition of a framework: “an essential supporting structure” — frameworks that concern themselves with the more abstract things like code organization and tackling code complexity. Even though Object-Oriented and Functional Programming are both programming paradigms, they’re also both very high-level frameworks.

Limiting our choices

C++ is a horrible [object-oriented] language… And limiting your project to C means that people don’t screw things up with any idiotic “object model” c&@p.
— Linus Torvalds, the creator of Linux

Linus Torvalds is widely known for his open criticism of C++ and OOP. One thing he was 100% right about is limiting programmers in the choices they can make. In fact, the fewer choices programmers have, the more resilient their code becomes. In the quote above, Linus Torvalds highly recommends having a good framework to base our code upon.

Photo by specphotops on Unsplash

Many dislike speed limits on the roads, but they’re essential to help prevent people from crashing to death. Similarly, a good programming framework should provide mechanisms that prevent us from doing stupid things.

A good programming framework helps us to write reliable code. First and foremost, it should help reduce complexity by providing the following things:

  1. Modularity and reusability
  2. Proper state isolation
  3. High signal-to-noise ratio

Unfortunately, OOP provides developers too many tools and choices, without imposing the right kinds of limitations. Even though OOP promises to address modularity and improve reusability, it fails to deliver on its promises (more on this later). OOP code encourages the use of shared mutable state, which has been proven to be unsafe time and time again. OOP typically requires a lot of boilerplate code (low signal-to-noise ratio).

Functional programming

What exactly is Functional Programming? Some people consider it to be a highly complicated programming paradigm that is only applicable in academia and is not suitable for the “real-world”. This couldn’t be further from the truth!

Yes, Functional Programming has a strong mathematical foundation and takes its roots in lambda calculus. However, most of its ideas emerged as a response to the weaknesses in the more mainstream programming languages. Functions are the core abstraction of Functional Programming. When used properly, functions provide a level of code modularity and reusability never seen in OOP. It even features design patterns that address the issues of nullability and provides a superior way of error handling.

The one thing that Functional Programming does really well is it helps us write reliable software. The need for a debugger almost disappears completely. Yep, no need to step through your code and watch variables. I personally haven’t touched a debugger in a very long time.

The best part? If you already know how to use functions, then you’re already a functional programmer. You just need to learn how to make the best use of those functions!

I’m not preaching Functional Programming, I don’t really care what programming paradigm you use writing your code. I’m simply trying to convey the mechanisms that Functional Programming provides to address the problems inherent with OOP/imperative programming.

We Got OOP All Wrong
I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is messaging.
- Alan Kay, the inventor of OOP

Erlang is not usually thought of as an Object-Oriented language. But probably Erlang is the only mainstream Object-Oriented language out there. Yes, of course Smalltalk is a proper OOP language — however, it is not in wide use. Both Smalltalk and Erlang make use of OOP the way it was originally intended by its inventor, Alan Kay.

Messaging

Alan Kay coined the term “Object Oriented Programming” in the 1960s. He had a background in biology and was attempting to make computer programs communicate the same way living cells do.

Photo by Muukii on Unsplash

Alan Kay’s big idea was to have independent programs (cells) communicate by sending messages to each other. The state of the independent programs would never be shared with the outside world (encapsulation).

That’s it. OOP was never intended to have things like inheritance, polymorphism, the “new” keyword, and the myriad of design patterns.

OOP in its purest form

Erlang is OOP in its purest form. Unlike more mainstream languages, it focuses on the core idea of OOP — messaging. In Erlang, objects communicate by passing immutable messages between objects.

Is there proof that immutable messages are a superior approach compared to method calls?

Hell yes! Erlang is probably the most reliable language in the world. It powers most of the world’s telecom (and hence the internet) infrastructure. Some of the systems written in Erlang have reliability of 99.9999999% (you read that right — nine nines).

Code Complexity
With OOP-inflected programming languages, computer software becomes more verbose, less readable, less descriptive, and harder to modify and maintain.
Richard Mansfield

The most important aspect of software development is keeping the code complexity down. Period. None of the fancy features matter if the codebase becomes impossible to maintain. Even 100% test coverage is worth nothing if the codebase becomes too complex and unmaintainable.

What makes the codebase complex? There are many things to consider, but in my opinion, the top offenders are: shared mutable state, erroneous abstractions, and low signal-to-noise ratio (often caused by boilerplate code). All of them are prevalent in OOP.

The Problems of State

Photo by Mika Baumeister on Unsplash

What is state? Simply put, state is any temporary data stored in memory. Think variables or fields/properties in OOP. Imperative programming (including OOP) describes computation in terms of the program state and changes to that state. Declarative (functional) programming describes the desired results instead, and don’t specify changes to the state explicitly.

Mutable State — the act of mental juggling

I think that large objected-oriented programs struggle with increasing complexity as you build this large object graph of mutable objects. You know, trying to understand and keep in your mind what will happen when you call a method and what will the side effects be.
Rich Hickey, creator of Clojure

Image source: https://www.flickr.com/photos/[email protected]/8707342427

State by itself is quite harmless. However, mutable state is the big offender. Especially if it is shared. What exactly is mutable state? Any state that can change. Think variables or fields in OOP.

Real-world example, please!

You have a blank piece of paper, you write a note on it, and you end up with the same piece of paper in a different state (text). You, effectively, have mutated the state of that piece of paper.

That is completely fine in the real world since nobody else probably cares about that piece of paper. Unless this piece of paper is the original Mona Lisa painting.

Limitations of the Human Brain

Why is mutable state such a big problem? The human brain is the most powerful machine in the known universe. However, our brains are really bad at working with state since we can only hold about 5 items at a time in our working memory. It is much easier to reason about a piece of code if you only think about what the code does, not what variables it changes around the codebase.

Programming with mutable state is an act of mental juggling️. I don’t know about you, but I could probably juggle two balls. Give me three or more balls and I will certainly drop all of them. Why are we then trying to perform this act of mental juggling every single day at work?

Unfortunately, the mental juggling of mutable state is at the very core of OOP . The sole purpose for the existence of methods on an object is to mutate that same object.

Scattered state

Photo by Markus Spiske on Unsplash

OOP makes the problem of code organization even worse by scattering state all over the program. The scattered state is then shared promiscuously between various objects.

Real-world example, please!

Let’s forget for a second that we’re all grown-ups, and pretend we’re trying to assemble a cool lego truck.

However, there’s a catch — all the truck parts are randomly mixed with parts from your other lego toys. And they have been put in 50 different boxes, randomly again. And you’re not allowed to group your truck parts together — you have to keep in your head where the various truck parts are, and can only take them out one by one.

Yes, you will eventually assemble that truck, but how long will it take you?

How does this relate to programming?

In Functional Programming, state typically is being isolated. You always know where some state is coming from. State is never scattered across your different functions. In OOP, every object has its own state, and when building a program , you have to keep in mind the state of all of the objects that you currently are working with.

To make our lives easier, it is best to have only a very small portion of the codebase deal with state. Let the core parts of your application be stateless and pure. This actually is the main reason for the huge success of the flux pattern on the frontend (aka Redux).

Promiscuously shared state

As if our lives aren’t already hard enough because of having scattered mutable state, OOP goes one step further!

Real-world Example, Please!

Mutable state in the real world is almost never a problem, since things are kept private and never shared. This is “proper encapsulation” at work. Imagine a painter who is working on the next Mona Lisa painting. He is working on the painting alone, finishes up, and then sells his masterpiece for millions.

Now, he’s bored with all that money and decides to do things a little bit differently. He thinks that it would be a good idea to have a painting party. He invites his friends elf, Gandalf, policeman, and a zombie to help him out. Teamwork! They all start painting on the same canvas at the same time. Of course, nothing good comes out of it — the painting is a complete disaster!

Shared mutable state makes no sense in the real world. Yet this is exactly what happens in OOP programs — state is promiscuously shared between various objects, and they mutate it in any way they see fit. This, in turn, makes reasoning about the program harder and harder as the codebase keeps growing.

Concurrency issues

The promiscuous sharing of mutable state in OOP code makes parallelizing such code almost impossible. Complex mechanisms have been invented in order to address this problem. Thread locking, mutex, and many other mechanisms have been invented. Of course, such complex approaches have their own drawbacks — deadlocks, lack of composability, debugging multi-threaded code is very hard and time-consuming. I’m not even talking about the increased complexity caused by making use of such concurrency mechanisms.

Not all state is evil

Is all state evil? No, Alan Kay state probably is not evil! State mutation probably is fine if it is truly isolated (not the “OOP-way” isolated).

It is also completely fine to have immutable data-transfer-objects. The key here is “immutable”. Such objects are then used to pass data between functions.

However, such objects would also make OOP methods and properties completely redundant. What’s the use in having methods and properties on an object if it cannot be mutated?

Mutability is Inherent to OOP

Some might argue that mutable state is a design choice in OOP, not an obligation. There is a problem with that statement. It is not a design choice, but pretty much the only option. Yes, one can pass immutable objects to methods in Java/C#, but this is rarely done since most of the developers default to data mutation. Even if developers attempt to make proper use of immutability in their OOP programs, the languages provide no built-in mechanisms for immutability, and for working effectively with immutable data (i.e. persistent data structures).

Yes, we can ensure that objects communicate only by passing immutable messages and never pass any references (which is rarely done). Such programs would be more reliable than mainstream OOP. However, the objects still have to mutate their own state once a message has been received. A message is a side effect, and its single purpose is to cause changes. Messages would be useless if they couldn’t mutate the state of other objects.

It is impossible to make use of OOP without causing state mutations.

The Trojan Horse of Encapsulation

Photo by Jamie McInall from Pexels

We’ve been told that encapsulation is one of the greatest benefits of OOP. It is supposed to protect the object’s internal state from outside access. There’s a small problem with this though. It doesn’t work.

Encapsulation is the trojan horse of OOP. It sells the idea of shared mutable state by making it appear safe. Encapsulation allows (and even encourages) unsafe code to sneak into our codebase, making the codebase rot from within.

The global state problem

We’ve been told that global state is the root of all evil. It should be avoided at all costs. What we have never been told is that encapsulation, in fact, is glorified global state.

To make the code more efficient, objects are passed not by their value, but by their reference. This is where “dependency injection” falls flat.

Let me explain. Whenever we create an object in OOP, we pass references to its dependencies to the constructor. Those dependencies also have their own internal state. The newly created object happily stores references to those dependencies in its internal state and is then happy to modify them in any way it pleases. And it also passes those references down to anything else it might end up using.

This creates a complex graph of promiscuously shared objects that all end up changing each other’s state. This, in turn, causes huge problems since it becomes almost impossible to see what caused the program state to change. Days might be wasted trying to debug such state changes. And you’re lucky if you don’t have to deal with concurrency (more on this later).

Methods/Properties

The methods or properties that provide access to particular fields are no better than changing the value of a field directly. It doesn’t matter whether you mutate an object’s state by using a fancy property or method — the result is the same: mutated state.

The Problem with Real World Modeling

Photo by Markus Spiske on Unsplash

Some people say that OOP tries to model the real world. This is simply not true — OOP has nothing to relate to in the real world. Trying to model programs as objects probably is one of the biggest OOP mistakes.

The real world is not hierarchical

OOP attempts to model everything as a hierarchy of objects. Unfortunately, that is not how things work in the real world. Objects in the real world interact with each other using messages, but they mostly are independent of each other.

Inheritance in the real world

OOP inheritance is not modeled after the real world. The parent object in the real world is unable to change the behavior of child objects at run-time. Even though you inherit your DNA from your parents, they’re unable to make changes to your DNA as they please. You do not inherit “behaviors” from your parents, you develop your own behaviors. And you’re unable to “override” your parents’ behaviors.

The real world has no methods

Does the piece of paper you’re writing on have a “write” method? No! You take an empty piece of paper, pick up a pen, and write some text. You, as a person, don’t have a “write” method either — you make the decision to write some text based on outside events or your internal thoughts.

The Kingdom of Nouns
Objects bind functions and data structures together in indivisible units. I think this is a fundamental error since functions and data structures belong in totally different worlds.
Joe Armstrong, creator of Erlang

Photo by Cederic X on Unsplash

Objects (or nouns) are at the very core of OOP. A fundamental limitation of OOP is that it forces everything into nouns. And not everything should be modeled as nouns. Operations (functions) should not be modeled as objects. Why are we forced to create a Multiplierclass when all we need is a function that multiplies two numbers? Simply have a Multiply function, let data be data and let functions be functions!

In non-OOP languages, doing trivial things like saving data to a file is straightforward — very similar to how you would describe an action in plain English.

Real-world example, please!

Sure, going back to the painter example, the painter owns a PaintingFactory. He has hired a dedicated BrushManager , ColorManager, a CanvasManager and a MonaLisaProvider. His good friend zombie makes use of a BrainConsumingStrategy . Those objects, in turn, define the following methods: CreatePainting , FindBrush , PickColor , CallMonaLisa , and ConsumeBrainz.

Of course, this is plain stupidity, and could never have happened in the real world. How much unnecessary complexity has been created for the simple act of drawing a painting?

There’s no need to invent strange concepts to hold your functions when they’re allowed to exist separately from the objects.

Unit Testing

Photo by Ani Kolleshi on Unsplash

Automated testing is an important part of the development process and helps tremendously in preventing regressions (i.e. bugs being introduced into existing code). Unit Testing plays a huge role in the process of automated testing.

Some might disagree, but OOP code is notoriously difficult to unit test. Unit Testing assumes testing things in isolation, and to make a method unit-testable:

  1. Its dependencies have to be extracted into a separate class.
  2. Create an interface for the newly created class.
  3. Declare fields to hold the instance of the newly created class.
  4. Make use of a mocking framework to mock the dependencies.
  5. Make use of a dependency-injection framework to inject the dependencies.

How much more complexity has to be created just to make a piece of code testable? How much time was wasted just to make some code testable?

> PS we’d also have to instantiate the entire class in order to test a single method. This will also bring in the code from all of its parent classes.

With OOP, writing tests for legacy code is even harder — almost impossible. Entire companies have been created (TypeMock) around the issue of testing legacy OOP code.

Boilerplate code

Boilerplate code is probably the biggest offender when it comes to the signal-to-noise ratio. Boilerplate code is “noise” that is required to get the program to compile. Boilerplate code takes time to write and makes the codebase less readable because of the added noise.

While “program to an interface, not to an implementation” is the recommended approach in OOP, not everything should become an interface. We’d have to resort to using interfaces in the entire codebase, for the sole purpose of testability. We’d also probably have to make use of dependency injection, which further introduced unnecessary complexity.

Testing private methods

Some people say that private methods shouldn’t be tested… I tend to disagree, unit testing is called “unit” for a reason — test small units of code in isolation. Yet testing of private methods in OOP is nearly impossible. We shouldn’t be making private methodsinternal just for the sake of testability.

In order to achieve testability of private methods, they usually have to be extracted into a separate object. This, in turn, introduces unnecessary complexity and boilerplate code.

Refactoring

Refactoring is an important part of a developer’s day-to-day job. Ironically, OOP code is notoriously hard to refactor. Refactoring is supposed to make the code less complex, and more maintainable. On the contrary, refactored OOP code becomes significantly more complex — to make the code testable, we’d have to make use of dependency injection, and create an interface for the refactored class. Even then, refactoring OOP code is really hard without dedicated tools like Resharper.

// before refactoring:
public class CalculatorForm {
    private string aText, bText;
private bool IsValidInput(string text) =&gt; true;

private void btnAddClick(object sender, EventArgs e) {
    if ( !IsValidInput(bText) || !IsValidInput(aText) ) {
        return;
    }
}

}

// after refactoring:
public class CalculatorForm {
private string aText, bText;

private readonly IInputValidator _inputValidator;

public CalculatorForm(IInputValidator inputValidator) {
    _inputValidator = inputValidator;
}

private void btnAddClick(object sender, EventArgs e) {
    if ( !_inputValidator.IsValidInput(bText)
        || !_inputValidator.IsValidInput(aText) ) {
        return;
    }
}

}

public interface IInputValidator {
bool IsValidInput(string text);
}

public class InputValidator : IInputValidator {
public bool IsValidInput(string text) => true;
}

public class InputValidatorFactory {
public IInputValidator CreateInputValidator() => new InputValidator();
}

In the simple example above, the line count has more than doubled just to extract a single method. Why does refactoring create even more complexity, when the code is being refactored in order to decrease complexity in the first place?

Contrast this to a similar refactor of non-OOP code in JavaScript:

// before refactoring:

// calculator.js:
const isValidInput = text => true;

const btnAddClick = (aText, bText) => {
if (!isValidInput(aText) || !isValidInput(bText)) {
return;
}
}

// after refactoring:

// inputValidator.js:
export const isValidInput = text => true;

// calculator.js:
import { isValidInput } from './inputValidator';

const btnAddClick = (aText, bText, _isValidInput = isValidInput) => {
if (!_isValidInput(aText) || !_isValidInput(bText)) {
return;
}
}

The code has literally stayed the same — we simply moved the isValidInput function to a different file and added a single line to import that function. We’ve also added _isValidInput to the function signature for the sake of testability.

This is a simple example, but in practice the complexity grows exponentially as the codebase gets bigger.

And that’s not all. Refactoring OOP code is extremely risky. Complex dependency graphs and state scattered all over OOP codebase, make it impossible for the human brain to consider all of the potential issues.

The Band-aids

Image source: Photo by Pixabay from Pexels

What do we do when something is not working? It is simple, we only have two options — throw it away or try fixing it. OOP is something that can’t be thrown away easily, millions of developers are trained in OOP. And millions of organizations worldwide are using OOP.

You probably see now that OOP doesn’t really work, it makes our code complex and unreliable. And you’re not alone! People have been thinking hard for decades trying to address the issues prevalent in OOP code. They’ve come up with a myriad of design patterns.

Design patterns

OOP provides a set of guidelines that should theoretically allow developers to incrementally build larger and larger systems: SOLID principle, dependency injection, design patterns, and others.

Unfortunately, the design patterns are nothing other than band-aids. They exist solely to address the shortcomings of OOP. A myriad of books has even been written on the topic. They wouldn’t have been so bad, had they not been responsible for the introduction of enormous complexity to our codebases.

The problem factory

In fact, it is impossible to write good and maintainable Object-Oriented code.

On one side of the spectrum we have an OOP codebase that is inconsistent and doesn’t seem to adhere to any standards. On the other side of the spectrum, we have a tower of over-engineered code, a bunch of erroneous abstractions built one on top of one another. Design patterns are very helpful in building such towers of abstractions.

Soon, adding in new functionality, and even making sense of all the complexity, gets harder and harder. The codebase will be full of things like SimpleBeanFactoryAwareAspectInstanceFactory, AbstractInterceptorDrivenBeanDefinitionDecorator, TransactionAwarePersistenceManagerFactoryProxyorRequestProcessorFactoryFactory .

Precious brainpower has to be wasted trying to understand the tower of abstractions that the developers themselves have created. The absence of structure is in many cases better than having bad structure (if you ask me).

Image source: https://www.reddit.com/r/ProgrammerHumor/comments/418x95/theory_vs_reality/

Further reading: FizzBuzzEnterpriseEdition

The Fall of the Four OOP Pillars

The four pillars of OOP are: Abstraction, Inheritance, Encapsulation, and Polymorphism.

Let’s see what they really are, one-by-one.

Inheritance

I think the lack of reusability comes in object-oriented languages, not in functional languages. Because the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
— Joe Armstrong, creator of Erlang

OOP inheritance has nothing to do with the real world. Inheritance, in fact, is an inferior way to achieve code reusability. The gang of four has explicitly recommended preferring composition over inheritance. Some modern programming languages avoid inheritance altogether.

There are a few problems with inheritance:

  1. Bringing in a lot of code that your class doesn’t even need (banana and the jungle problem).
  2. Having parts of your class defined somewhere else makes the code hard to reason about, especially with multiple levels of inheritance.
  3. In most programming languages, multiple inheritance isn’t even possible. This mostly renders inheritance useless as a code-sharing mechanism.

OOP polymorphism

Polymorphism is great, it allows us to change program behavior at runtime. However, it is a very basic concept in computer programming. I’m not too sure why OOP focuses so much on polymorphism. OOP polymorphism gets the job done but once again it results in the act of mental juggling. It makes the codebase significantly more complex, and reasoning about the concrete method that is being invoked becomes really hard.

Functional programming, on the other hand, allows us to achieve the same polymorphism in a much more elegant way…by simply passing in a function that defines the desired runtime behavior. What could be simpler than that? No need to define a bunch of overloaded abstract virtual methods in multiple files (and the interface).

Encapsulation

As we discussed earlier, encapsulation is the trojan horse of OOP. It is actually a glorified global mutable state and makes the unsafe code appear safe. An unsafe coding practice is a pillar that OOP programmers rely on in their day-to-day job…

Abstraction

Abstraction in OOP attempts to tackle complexity by hiding unnecessary details from the programmer. Theoretically, it should allow the developer to reason about the codebase without having to think about the hidden complexity.

I don’t even know what to say…a fancy word for a simple concept. In procedural/functional languages we can simply “hide” the implementation details in a neighboring file. No need to call this basic act an “abstraction”.

For more details on the fall of OOP pillars, please read Goodbye, Object Oriented Programming

Why Does OOP Dominate the Industry?

The answer is simple, the reptiloid alien race has conspired with the NSA (and the Russians) to torture us programmers to death…

Photo by Gaetano Cessati on Unsplash

But seriously, Java is probably the answer.

Java is the most distressing thing to happen to computing since MS-DOS.
- Alan Kay, the inventor of object-oriented programming

Java was Simple

When it was first introduced in 1995, Java was a very simple programming language, compared to the alternatives. At that time, the barrier of entry for writing desktop applications was high. Developing desktop applications involved writing low-level win32 APIs in C, and developers also had to concern themselves with manual memory management. The other alternative was Visual Basic, but many probably didn’t want to lock themselves into the Microsoft ecosystem.

When Java was introduced, it was a no-brainer for many developers since it was free, and could be used across all platforms. Things like built-in garbage collection, friendly-named APIs (compared to the cryptic win32 APIs), proper namespaces, and familiar C-like syntax made Java even more approachable.

GUI programming was also becoming more popular, and it seemed that various UI components mapped well to classes. Method autocompletion in the IDEs also made people claim that OOP APIs are easier to use.

Perhaps Java wouldn’t have been so bad had it not forced OOP on developers. Everything else about Java seemed pretty good. Its garbage collection, portability, exception handling features, which other mainstream programming languages lacked, were really great in 1995,

Then C# came along

Initially, Microsoft had been relying heavily on Java. When things started getting awry (and after a long legal battle with Sun Microsystems over Java licensing), Microsoft decided to invest in its own version of Java. That is when C# 1.0 was born. C# as a language has always been thought of as “the better Java”. However, there’s one huge problem — it was the same OOP language with the same flaws, hidden under a slightly improved syntax.

Microsoft has been investing heavily in its .NET ecosystem, which also included good developer tooling. For years Visual Studio has probably been one of the best IDEs available. This, in turn, has led to wide-spread adoption of the .NET framework, especially in the enterprise.

More recently Microsoft has been investing heavily in the browser ecosystem, by pushing its TypeScript. TypeScript is great because it can compile pure JavaScript and adds in things like static type checking. What’s not so great about it is it has no proper support for functional constructs — no built-in immutable data structures, no function composition, no proper pattern matching. TypeScript is OOP-first, and mostly is C# for the browser. Anders Hejlsberg was even responsible for the design of both C# and TypeScript.

Functional languages

Functional languages, on the other hand, have never been backed by someone as big as Microsoft. F# doesn’t count since the investment was minuscule. The development of functional languages is mostly community-driven. This probably explains the differences in popularity between OOP and FP languages.

Time to Move On?

We now know that OOP is an experiment that failed. It is time to move on. It is time that we, as a community, admit that this idea has failed us, and we must give up on it.
- Lawrence Krubner

Photo by SpaceX on Unsplash

Why are we stuck using something that fundamentally is a suboptimal way to organize programs? Is this plain ignorance? I doubt it, the people working in software engineering aren’t stupid. Are we perhaps more worried about “looking smart” in the face of our peers by making use of fancy OOP terms like “design patterns”, “abstraction”, “encapsulation”, “polymorphism” and “interface segregation”? Probably not.

I think that it’s really easy to continue using something that we’ve been using for decades. Most of the people have never really tried Functional Programming. Those who have (like myself) can never go back to writing OOP code.

Henry Ford once famously said — “If I had asked people what they wanted, they would have said faster horses”. In the world of software, most people would probably want a “better OOP language”. People can easily describe a problem they’re having (getting the codebase organized and less complex), but not the best solution.

What Are the Alternatives?

Spoiler alert: Functional Programming.

Photo by Harley-Davidson on Unsplash

If terms like functors and monads make you a little uneasy, then you’re not alone! Functional Programming wouldn’t have been so scary had they given more intuitive names to some of its concepts. Functor? That’s simply something we can transform with a function, think list.map. Monad? Simply computations that can be chained!

Trying out Functional Programming will make you a better developer. You will finally have the time to write real code that solves real-world problems, rather than having to spend most of your time thinking about abstractions and design patterns.

You might not realize this, but you already are a functional programmer. Are you using functions in your day-to-day work? Yes? Then you’re already a functional programmer! You just have to learn how to make the best use of those functions.

Two great functional languages with a very gentle learning curve are Elixir and Elm. They let the developer focus on what matters most — writing reliable software while removing all of the complexity that more traditional functional languages have.

What are the other options? Is your organization already is using C#? Give F# a try — it is an amazing functional language, and provides great interoperability with the existing .NET code. Using Java? Then using Scala or Clojure are both really good options. Using JavaScript? With the right guidance and linting, JavaScript can be a good functional language.

The Defenders of OOP

Photo by Ott Maidre from Pexels

I expect some sort of reaction from the defenders of OOP. They will say that this article is full of inaccuracies. Some might even start calling names. They might even call me a “junior” developer with no real-world OOP experience. Some might say that my assumptions are erroneous, and examples are useless. Whatever.

They have the right to their opinion. However, their arguments in the defense of OOP are usually quite weak. It is ironic that most of them probably have never really programmed in a true functional language. How can someone draw comparisons between two things if you have never really tried both? Such comparisons aren’t very useful.

The Law of Demeter is not very useful — it does nothing to address the issue of non-determinism, shared mutable state is still shared mutable state, no matter how you access or mutate that state. a.total()is not much better than a.getB().getC().total(). It simply sweeps the problem under the rug.

Domain-Driven Design? That’s a useful design methodology, it helps a bit with the complexity. However, it still does nothing to address the fundamental issue of shared mutable state.

Just a tool in a toolbox…

I often hear people say that OOP is just another tool in a toolbox. Yes, it is as much a tool in a toolbox as horses and cars are both tools for transportation… After all, they all serve the same purpose, right? Why use cars when we can continue riding good old horses?

History repeats itself

This actually reminds me of something. At the beginning of the 20th century, automobiles started replacing the horses. In the year 1900 New York had only a few cars on the roads, people have been using horses for transportation. In the year 1917, no more horses were left on the roads. A huge industry was centered around horse transportation. Entire businesses have been created around things like manure cleaning.

And people resisted change. They called automobiles another “fad” that eventually pass. After all, horses have been here for centuries! Some even asked the government to intervene.

How is this relevant? The software industry is centered around OOP. Millions of people are trained in OOP, and millions of companies make use of OOP in their code. Of course, they will try to discredit anything that threatens their bread-and-butter! It’s just common sense.

We clearly see the history repeating itself — in the 20th century it was the horses vs automobiles, in the 21st century it is Object-Oriented vs Functional Programming.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Goodbye, Object Oriented Programming

JavaScript and Object-Oriented Programming

Python vs Java: Understand Object Oriented Programming


Goodbye, Object Oriented Programming

Goodbye, Object Oriented Programming

I’ve been programming in Object Oriented languages for decades. The first OO language I used was C++ and then Smalltalk and finally .NET and Java.

I’ve been programming in Object Oriented languages for decades. The first OO language I used was C++ and then Smalltalk and finally .NET and Java.

I was gung-ho to leverage the benefits of Inheritance, Encapsulation, and Polymorphism. The Three Pillars of the Paradigm.

I was eager to gain the promise of Reuse and leverage the wisdom gained by those who came before me in this new and exciting landscape.

I couldn’t contain my excitement at the thought of mapping my real-world objects into their Classes and expected the whole world to fall neatly into place.

I couldn’t have been more wrong.

Inheritance, the First Pillar to Fall

At first glance, Inheritance appears to be the biggest benefit of the Object Oriented Paradigm. All the simplistic examples of shape hierarchies that are paraded out as examples to the newly indoctrinated seem to make logical sense.

And Reuse is the word of the day. No… make that the year and perhaps evermore.

I swallowed this whole and rushed out into the world with my newfound insight.

Banana Monkey Jungle Problem

With religion in my heart and problems to solve, I started building Class Hierarchies and writing code. And all was right with the world.

I’ll never forget that day when I was ready to cash in on the promise of Reuse by inheriting from an existing class. This was the moment I had been waiting for.

A new project came along and I thought back to that Class that I was so fond of in my last project.

No problem. Reuse to the rescue. All I gotta do is simply grab that Class from the other project and use it.

Well… actually… not just that Class. We’re gonna need the parent Class. But… But that’s it.

Ugh… Wait… Looks like we gonna also need the parent’s parent too… And then… We’re going to need ALL of the parents. Okay… Okay… I handle this. No problem.

And great. Now it won’t compile. Why?? Oh, I see… This object contains this other object. So I’m gonna need that too. No problem.

Wait… I don’t just need that object. I need the object’s parent and its parent’s parent and so on and so on with every contained object and ALL the parents of what those contain along with their parent’s, parent’s, parent’s…

Ugh.

There’s a great quote by Joe Armstrong, the creator of Erlang:

The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.#### Banana Monkey Jungle Solution

I can tame this problem by not creating hierarchies that are too deep. But if Inheritance is the key to Reuse, then any limits I place on that mechanism will surely limit the benefits of Reuse. Right?

Right.

So what’s a poor Object Oriented Programmer, who’s had a healthy helping of the Kool-aid, to do?

Contain and Delegate. More on this later.

The Diamond Problem

Sooner or later, the following problem will rear its ugly and, depending on the language, unsolvable head.

Most OO languages do not support this, even though this seems to make logical sense. What’s so difficult about supporting this in OO languages?

Well, imagine the following pseudocode:

Class PoweredDevice {
}

Class Scanner inherits from PoweredDevice {
  function start() {
  }
}

Class Printer inherits from PoweredDevice {
  function start() {
  }
}

Class Copier inherits from Scanner, Printer {
}

Notice that both the Scanner class and the Printer class implement a function called start.

So which start function does the Copier class inherit? The Scanner one? The Printer one? It can’t be both.

The Diamond Solution

The solution is simple. Don’t do that.

Yes that’s right. Most OO languages don’t let you do this.

But, but… what if I have to model this? I want my Reuse!

Then you must Contain and Delegate.

Class PoweredDevice {
}

Class Scanner inherits from PoweredDevice {
  function start() {
  }
}

Class Printer inherits from PoweredDevice {
  function start() {
  }
}

Class Copier {
  Scanner scanner
  Printer printer
  function start() {
    printer.start()
  }
}

Notice here that the Copier class now contains an instance of a Printer and of a Scanner. It delegates the start function to the Printer class’s implementation. It could just as easily delegated to the Scanner.

This problem is yet another crack in the Inheritance pillar.

The Fragile Base Class Problem

So I’m making my hierarchies shallow and keeping them from being cyclical. No diamonds for me.

And all was right with the world. That is until…

One day, my code works and the next day it stops working. Here’s the kicker. I didn’t change my code.

Well, maybe it’s a bug… But wait… Something did change

But it wasn’t in my code. Turns out the change was in the class that I inherited from.

How could a change in the Base class break my code??

This is how…

Imagine the following Base class (It’s written in Java, but it should be easy to understand if you don’t know Java):

import java.util.ArrayList;
 
public class Array
{
  private ArrayList<Object> a = new ArrayList<Object>();
 
  public void add(Object element)
  {
    a.add(element);
  }
 
  public void addAll(Object elements[])
  {
    for (int i = 0; i < elements.length; ++i)
      a.add(elements[i]); // this line is going to be changed
  }
}

IMPORTANT: Notice the commented line of code. This line is going to be changed later which will break things.

This class has 2 functions on its interface, add() and addAll(). The add() function will add a single element and addAll() will add multiple elements by calling the add function.

And here’s the Derived class:

public class ArrayCount extends Array
{
  private int count = 0;
 
  @Override
  public void add(Object element)
  {
    super.add(element);
    ++count;
  }
 
  @Override
  public void addAll(Object elements[])
  {
    super.addAll(elements);
    count += elements.length;
  }
}

The ArrayCount class is a specialization of the general Array class. The only behavioral difference is that the ArrayCount keeps a count of the number of elements.

Let’s look at both of these classes in detail.

The Array add() adds an element to a local ArrayList.

The Array addAll() calls the local ArrayList add for each element.

The ArrayCount****add()** calls its parent’s add() and then increments the count.

The ArrayCount addAll() calls its parent’s addAll() and then increments the count by the number of elements.

And all works fine.

Now for the breaking change. The commented line of code in the Base class is changed to the following:

  public void addAll(Object elements[])
  {
    for (int i = 0; i < elements.length; ++i)
      add(elements[i]); // this line was changed
  }

As far as the owner of the Base class is concerned, it still functions as advertised. And all of the automated tests still pass.

But the owner is oblivious to the Derived class. And the owner of Derived class is in for a rude awakening.

Now ArrayCount addAll() calls its parent’s addAll() which internally calls the add() which has been OVERRIDEN by the Derived class.

This causes the count to be incremented each time the Derived class’s add() is called and then it’s incremented AGAIN by the number of elements that were added in the Derived class’s addAll().

IT’S COUNTED TWICE.

If this can happen, and it does, the author of the Derived class must KNOW how the Base class has been implemented. And they must be informed about every change in the Base class since it could break their Derived class in unpredictable ways.

Ugh! This huge crack is forever threatening the stability of precious Inheritance pillar.

The Fragile Base Class Solution

Once again Contain and Delegate to the rescue.

By using Contain and Delegate, we go from White Box programming to Black Box programming. With White Box programming, we have to look at the implementation of the base class.

With Black Box programming, we can be completely ignorant of the implementation since we cannot inject code into the Base class by overriding one of its functions. We only have to concern ourselves with the Interface.

This trend is disturbing…

Inheritance was supposed to be a huge win for Reuse.

Object Oriented languages don’t make Contain and Delegate easy to do. They were designed to make Inheritance easy.

If you’re like me, you’re starting to wonder about this Inheritance thing. But more important, this should shake your confidence in the power of Classification via Hierarchies.

The Hierarchy Problem

Every time I start at a new company, I struggle with the problem when I’m creating a place to put my Company Documents, e.g. the Employee Handbook.

Do I create a folder called Documents and then create a folder called Company in that?

Or do I create a folder called Company and then create a folder called Documents in that?

Both work. But which is right? Which is best?

The idea of Categorical Hierarchies was that there were Base Classes (parents) that were more general and that Derived Classes (children) were more specialized versions of those classes. And even more specialized as we make our way down the inheritance chain. (See the Shape Hierarchy above)

But if a parent and child could arbitrarily switch places, then clearly something is wrong with this model.

The Hierarchy Solution

What’s wrong is…

Categorical Hierarchies don’t work.

So what are hierarchies good for?

Containment.

If you look at the real world, you’ll see Containment (or Exclusive Ownership) Hierarchies everywhere.

What you won’t find is Categorical Hierarchies. Let that sink in for a moment. The Object Oriented Paradigm was predicated upon the real world, one filled with Objects. But then it uses a broken model, viz. Categorical Hierarchies, where there is no real-world analogy.

But the real world is filled with Containment Hierarchies. A great example of a Containment Hierarchy is your socks. They are in a sock drawer which is contained in one drawer in your dresser which is contained in your bedroom which is contained in your house, etc.

Directories on your hard drive are another example of a Containment Hierarchy. They contains files.

So how do we categorize then?

Well, if you think of the Company Documents, it pretty much doesn’t matter where I put them. I can put them in a folder of Documents or a folder called Stuff.

The way I categorize it is with tags. I tag the file with the following tags:

Document
Company
Handbook

Tags have no order or hierarchy. (This solves the Diamond Problem too.)

Tags are analogous to interfaces since you can have multiple types associated with the document.

But with so many cracks, it looks like the Inheritance pillar has fallen.

Goodbye, Inheritance.

Encapsulation, the Second Pillar to Fall

At first glance, Encapsulation appears to be second biggest benefit of Object Oriented Programming.

Object state variables are protected from outside access, i.e. they’re Encapsulated in the Object.

No longer will we have to worry about global variables that are being accessed by who-knows-who.

Encapsulation is a Safe for your variables.

This Encapsulation thing is INCREDIBLE!!

Long live Encapsulation…

That is until…

The Reference Problem

For efficiency sake, Objects are passed to functions NOT by their value but by reference.

What that means is that functions will not pass the Object, but instead pass a reference or pointer to the Object.

If an Object is passed by reference to an Object Constructor, the constructor can put that Object reference in a private variable which is protected by Encapsulation.

But the passed Object is NOT safe!

Why not? Because some other piece of code has a pointer to the Object, viz. the code that called the Constructor. It MUST have a reference to the Object otherwise it couldn’t pass it to the Constructor?

The Reference Solution

The Constructor will have to Clone the passed in Object. And not a shallow clone but a deep clone, i.e. every object that is contained in the passed in Object and every object in those objects and so on and so on.

So much for efficiency.

And here’s the kicker. Not all objects can be Cloned. Some have Operating System resources associated with them making cloning useless at best or at worst impossible.

And EVERY single mainstream OO language has this problem.

Goodbye, Encapsulation.

Polymorphism, the Third Pillar to Fall

Polymorphism was the redheaded stepchild of the Object Oriented Trinity.

It’s sort of the Larry Fine of the group.

Everywhere they went he was there, but he was just a supporting character.

It’s not that Polymorphism isn’t great, it’s just that you don’t need an Object Oriented language to get this.

Interfaces will give you this. And without all of the baggage of OO.

And with Interfaces, there isn’t a limit to how many different behaviors you can mix in.

So without much ado, we say goodbye to OO Polymorphism and hello to interface-based Polymorphism.

Broken Promises

Well, OO sure promised a lot in the early days. And these promises are still being made to naive programmers sitting in classrooms, reading blogs and taking online courses.

It’s taken me years to realize how OO lied to me. I too was wide-eyed and inexperienced and trusting.

And I got burned.

Good-bye, Object Oriented Programming.

So then what?

Hello, Functional Programming. It’s been so nice to work with you over the past few years.

Just so you know, I’m NOT taking any of your promises at face value. I’m going to have to see it to believe it.

Once burned, twice shy and all.

You understand.

JavaScript and Object-Oriented Programming

JavaScript and Object-Oriented Programming

This article is written for students of JavaScript that don’t have any prior knowledge in object-oriented programming (OOP). I focus on the parts of OOP that are only relevant for JavaScript and not OOP in general. Therefore, I skip polymorphism because I think it fits better with a static-typed language.

This article is written for students of JavaScript that don’t have any prior knowledge in object-oriented programming (OOP). I focus on the parts of OOP that are only relevant for JavaScript and not OOP in general. Therefore, I skip polymorphism because I think it fits better with a static-typed language.

Why do you need to know this?

Have you picked JavaScript to be your first programming language? Do you want to be a hot-shot developer who works on giant enterprise systems spanning a hundred-thousand lines of code or more?

Unless you learn to fully embrace Object-Oriented Programming, you will be well and truly lost.

Different Mindsets

In football, you can play from a safe defense, you can play with high balls from the sides or you can attack like there is no tomorrow. All of these strategies have the same objective: To win the game.

The same is true for programming paradigms. There are different ways to approach a problem and design a solution.

Object-oriented programming, or OOP, is THE paradigm for modern application development and is supported by major languages like Java, C# or JavaScript.

The Object-Oriented Paradigm

From the OOP perspective, an application is a collection of “objects” that communicate with each other. We base these objects on things in the real world, like products in inventory or employee records. Objects contain data and perform some logic based on their data. As a result, OOP code is very easy to understand. What is not so easy is deciding how to break an application into these small objects in the first place.

If you are like me when I heard it the first time, you have no clue what this actually means — it all sounds very abstract. Feeling that way is absolutely fine. It’s more important that you’ve heard the idea, remember it, and try to apply OOP in your code. Over time, you will gain experience and align more of your code with this theoretical concept.

Lesson: OOP based on real-world objects lets anyone read your code and understand what’s going on.

Object as Centerpiece

A simple example will help you see how JavaScript implements the fundamental principles of OOP. Consider a shopping use case in which you put products into your basket and then calculate the total price you must pay. If you take your JavaScript knowledge and code the use case without OOP, it would look like this:

const bread = {name: 'Bread', price: 1};
const water = {name: 'Water', price: 0.25};

const basket = [];
basket.push(bread);
basket.push(bread);
basket.push(water);
basket.push(water);
basket.push(water);

const total = basket
  .map(product => product.price)
  .reduce((a, b) => a + b, 0);

console.log('one has to pay in total: ' + total);

The OOP perspective makes writing better code easier because we think of objects as we would encounter them in the real world. As our use case contains a basket of products, we already have two kinds of objects — the basket object and the product objects.

The OOP version of the shopping use case could be written like:

const bread = new Product("bread", 1);
const water = new Product("water", .25)

const basket = new Basket();
basket.addProduct(2, bread);
basket.addProduct(3, water);
basket.printShoppingInfo();

As you can see in the first line, we create a new object by using the keyword new followed by the name of what’s called a class (described below). This returns an object that we store to the variable bread. We repeat that for the variable water and take a similar path to create a variable basket. After you have added these products to your basket, you finally print out the total amount you have to pay.

The difference between the two code snippets is obvious. The OOP version almost reads like real English sentences and you can easily tell what’s going on.

Lesson: An object modeled on real-world things consists of data and functions.

Class as Template

We use classes in OOP as templates for creating objects. An object is an “instance of a class” and “instantiation” is the creation of an object based on a class. The code is defined in the class but can’t execute unless it is in a live object.

You can look at classes like the blueprints for a car. They define the car’s properties like torque and horsepower, internal functions such as air-to-fuel ratios and publicly accessible methods like the ignition. It is only when a factory instantiates the car, however, that you can turn the key and drive.

In our use case, we use the Product class to instantiate two objects, bread and water. Of course, those objects need code which you have to provide in the classes. It goes like this:

function Product(_name, _price) {
  const name = _name;
  const price = _price;

  this.getName = function() {
    return name;
  };

  this.getPrice = function() {
    return price;
  };
}

function Basket() {
  const products = [];

  this.addProduct = function(amount, product) {
    products.push(...Array(amount).fill(product));
  };

  this.calcTotal = function() {
    return products
      .map(product => product.getPrice())
      .reduce((a, b) => a + b, 0);
  };

  this.printShoppingInfo = function() {
    console.log('one has to pay in total: ' + this.calcTotal());
  };
}

A class in JavaScript looks like a function, but you use it differently. The name of the function is the class’s name and is capitalised. Since it doesn’t return anything, we don’t call the function in the usual way like const basket = Product("bread", 1);. Instead, we add the keyword new like const basket = new Product("bread", 1);.

The code inside the function is the constructor and is executed each time an object is instantiated. Product has the parameters _name and _price. Each new object stores these values inside of it.

Furthermore, we can define functions that the object will provide. We define these function by prepeding the this keyword which makes them accessible from the outside (see Encapsulation). Notice that the functions have full access to the properties.

Class Basket doesn’t require any arguments to create a new object. Instantiating a new Basket object simply generates an empty list of products that the program can fill afterwards.

Lesson: A class is a template for generating objects during runtime.

Encapsulation

You may encounter another version of how to declare a class:

function Product(name, price) {
  this.name = name;
  this.price = price;
}

Mind the assignment of the properties to the variable this. At first sight, it seems to be a better version because it doesn't require the getter (getName & getPrice) methods anymore and is therefore shorter.

Unfortunately, you have now given full access to the properties from the outside. So everybody could access and modify it:

const bread = new Product('bread', 1)
bread.price = -10;

This is something you don't want as it makes the application more difficult to maintain. What would happen if you added validation code to prevent, for example, prices less than zero? Any code that accesses the price property directly would bypass the validation. This could introduce errors that would be difficult to trace. Code that uses the object’s getter methods, on the other hand, are guaranteed to go through the object’s price validation.

Objects should have exclusive control over their data. In other words, the objects “encapsulate” their data and prevent other objects from accessing the data directly. The only way to access the data is indirectly via the functions written into the objects.

Data and processing (aka. logic) belong together. This is especially true when it comes to larger applications where it is very important that processing data is restricted to specifically-defined places.

Done right, the result OOP produces modularity by design, the holy grail in software development. It keeps away the feared spaghetti-code where everything is tightly coupled and you don’t know what happens when you change a small piece of code.

In our case, objects of class Product don’t let you change the price or the name after their initialisation. The instances of Product are read-only.

Lesson: Encapsulation prevents access to data except through the object’s functions.

Inheritance

Inheritance lets you create a new class by extending an existing class with additional properties and functions. The new class “inherits” all of the features of its parent, avoiding the creation of new code from scratch. Furthermore, any changes made to the parent class will automatically be available to the child class, making updates much easier.

Let’s say we have a new class called Book that has a name, a price and an author. With inheritance, you can say that a Book is the same as a Product but with the additional author property. We say that Product is the superclass of Book and Book is a subclass of Product:

function Book(_name, _price, _author) {
  Product.call(this, _name, _price);
  const author = _author;

  this.getAuthor = function() {
    return author;
  };

}

Note the additional Product.call along the this as first argument. Please be aware: Although book provides the getter methods, it still doesn’t have direct access to the properties name and price. Book must call that data from the Product class.

You can now add a book object to the basket without any issues:

const faust = new Book('faust', 12.5, 'Goethe');
basket.addProduct(1, faust);

Basket expects an object of type Product and, since book inherits from Product through Book, it is also a Product.

Lesson: Subclasses can inherit properties and functions from superclasses while adding properties and functions of their own.

JavaScript and OOP

You will find three different programming paradigms used to create JavaScript applications. They are Prototype-Based Programming, Object-Oriented Programming and Functional-Oriented Programming.

The reason for this lies in JavaScript’s history. Originally, it was prototype-based. JavaScript was not intended as a language for large applications.

Against the plan of its founders, developers increasingly used JavaScript for bigger applications. OOP was grafted on top of the original prototype-based technique.

The prototype-based approach is shown below and is seen as the "classical and default way" to construct classes. Unfortunately it does not support encapsulation.

Even though JavaScript’s support for OOP is not at the same level as other languages like Java, it is still evolving. The release of version ES6 added a dedicated class keyword we could use. Internally, it serves the same purpose as the prototype property, but it reduces the size of the code. However, ES6 classes still lack private properties, which is why I stuck to the “old way”.

For the sake of completeness, this is how we would write the Product, Basket and Book with ES6 classes and also with the prototype (classical and default) approach. Please note that these versions don't provide encapsulation:

// ES6 version

class Product {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }
}

class Book extends Product {
  constructor(name, price, author) {
    super(name, price);
    this.author = author;
  }
}

class Basket {
  constructor() {
    this.products = [];
  }

  addProduct(amount, product) {
    this.products.push(...Array(amount).fill(product));
  }

  calcTotal() {
    return this.products
      .map(product => product.price)
      .reduce((a, b) => a + b, 0);
  }

  printShoppingInfo() {
    console.log('one has to pay in total: ' + this.calcTotal());
  }
}

const bread = new Product('bread', 1);
const water = new Product('water', 0.25);
const faust = new Book('faust', 12.5, 'Goethe');

const basket = new Basket();
basket.addProduct(2, bread);
basket.addProduct(3, water);
basket.addProduct(1, faust);
basket.printShoppingInfo();
//Prototype version

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Book(name, price, author) {
  Product.call(this, name, price);
  this.author = author;
}
Book.prototype = Object.create(Product.prototype);
Book.prototype.constructor = Book;

function Basket() {
  this.products = [];
}
Basket.prototype.addProduct = function(amount, product) {
  this.products.push(...Array(amount).fill(product));
};
Basket.prototype.calcTotal = function() {
  return this.products
    .map(product => product.price)
    .reduce((a, b) => a + b, 0);
};
Basket.prototype.printShoppingInfo = function() {
  console.log('one has to pay in total: ' + this.calcTotal());
};

Lesson: OOP was added to JavaScript later in its development.

Summary

As a new programmer learning JavaScript, it will take time to fully appreciate Object-Oriented Programming. The important things to understand at this early stage are the principles the OOP paradigm is based on and the benefits they provide:

  • Objects modeled on real-world things are the centerpiece of any OOP-based application.
  • Encapsulation protects data from uncontrolled access.
  • Objects have functions that operate on the data the objects contain.
  • Classes are the templates used to instantiate objects.
  • Inheritance is a powerful tool for avoiding redundancy.
  • OOP is more verbose but easier to read than other coding paradigms.
  • Since OOP came later in JavaScript’s development, you may come across older code that uses prototype or functional programming techniques.