The Flexible Go, the Traditional Java, the Progressive Python, and the Dynamic Node.js: An Analogy for Understanding Interfaces in Different Languages

DSL
8 min readJan 8, 2023

--

Disclaimer: Please note that this post is not intended to suggest that other object-oriented languages are inferior to Go. Each programming language has its own strengths and weaknesses, and there are many factors to consider when choosing a language for a particular task. The purpose of this post is simply to compare the interface implementation features of Go to those of other object-oriented languages and discuss how they can be used in the context of hiring service providers.

Interfaces are a powerful feature of many programming languages that allow you to specify the behaviors that a type must implement. In Go, interfaces are satisfied implicitly, meaning that a type doesn’t need to explicitly declare that it implements an interface as long as it has the required methods. This provides a lot of flexibility as you will see . In this post, we’ll compare Go’s interfaces to those of other object-oriented languages and see how they stack up through an anaology of hiring the best service providers.

Go is the more flexible “Employer”

image is credited to https://achoicepainting.com/

Go is like a flexible and open-minded employer that is willing to hire any qualified candidate, regardless of their background or experience. It doesn’t matter if a candidate explicitly states that they have the required skills or not — as long as they can perform the job well, Go is happy to hire them.

Imagine that a company wants to hire service providers to perform various tasks. In Go, the company can specify the tasks that the service providers must be able to perform by defining an interface. The service providers can then implement the interface by providing implementations for the required methods. The company can then hire any service provider that satisfies the interface, regardless of whether the service provider explicitly declares that it implements the interface.

For example, the company might define the following interface for service providers that can paint buildings:

type Painter interface {
Paint(color string)
}

Now, any service provider that has a method named Paint with the correct signature can implement the Painter interface. For example, the following service provider implements the Painter interface:

type PaintingCompany struct {
// fields
}

func (c *PaintingCompany) Paint(color string) {
// implementation
}

Imagine that PaintingCompany is an actual popular painting company that the company wants to hire. The company can hire the PaintingCompany by creating a variable of the interface type Painter and assigning a value of type PaintingCompany to it:

pc := PaintingCompany{}
var p Painter = &pc

Now, the company can use the p variable to call the Paint method on the PaintingCompany service provider like this:

p.Paint("red")

Imagine that the company also wants to hire another service provider, DecoratingCompany, which can decorate buildings with wallpaper. The DecoratingCompany can implement the Painter interface by providing an implementation for the Paint method like this:

type DecoratingCompany struct {
// fields
}

func (c *DecoratingCompany) Paint(color string) {
// implementation
}

Now, the company can hire the DecoratingCompany by creating a variable of the interface type Painter and assigning a value of type DecoratingCompany to it:

dc := DecoratingCompany{}
var d Painter = &dc

The company can now use the d variable to call the Paint method on the DecoratingCompany service provider like this:

d.Paint("blue")

In this way, the company can hire any service provider that satisfies the Painter interface, regardless of whether the service provider explicitly declares that it implements the interface.

Java, the traditional and structured employer

Java is like a traditional and structured employer that values experience and qualifications. It only hires candidates who have explicitly declared that they have the skills and credentials needed for the job.

In Java, the company would need to explicitly declare that a service provider class implements the Painter interface. For example, the PaintingCompany class would need to declare that it implements the Painter interface like this:

public class PaintingCompany implements Painter {
// fields and methods
}

Here, the PaintingCompany class has a paint method with the correct signature to satisfy the Painter interface. However, it must explicitly declare that it implements the Painter interface using the implements keyword.

In this way, the company can only hire service providers that explicitly declare that they implement the Painter interface. For example, the company could hire the PaintingCompany in Java like this:

PaintingCompany pc = new PaintingCompany();
Painter p = pc;
p.paint("red");

Here, the PaintingCompany class is being assigned to a variable of the Painter interface type. Since the PaintingCompany class has declared that it implements the Painter interface, this assignment is valid. The company can then use the p variable to call the paint method on the PaintingCompany

Python, the modern and progressive employer

Python is like a modern and progressive employer that values diversity and inclusivity. It is open to hiring candidates from a wide range of backgrounds and experiences, as long as they can demonstrate their ability to do the job.

In Python the company would need to specify that the PaintingCompany class implements the Painter interface like this:

from abc import ABC, abstractmethod

class Painter(ABC):
@abstractmethod
def paint(self, color: str) -> None:
pass

Here, the Painter interface is defined using the ABC (abstract base class) and abstractmethod decorators from the abc module. The paint method is defined as an abstract method using the @abstractmethod decorator, which means that any class that inherits from the Painter interface must implement the paint method.

Now, imagine that the company wants to hire a service provider named PaintingCompany that can paint buildings. The PaintingCompany can implement the Painter interface by providing an implementation for the paint method like this:

class PaintingCompany(Painter):
def paint(self, color: str) -> None:
# implementation

Here, the PaintingCompany class has a paint method with the correct signature to satisfy the Painter interface. It also specifies that it inherits from the Painter interface using the parentheses after the class name.

Now, the company can hire the PaintingCompany by creating a variable of the interface type Painter and assigning a value of type PaintingCompany to it:

pc = PaintingCompany()
p: Painter = pc

Here, the variable p is of type Painter, and its value is an instance of the PaintingCompany class. Since the PaintingCompany class has specified that it implements the Painter interface, this assignment is valid. The company can then use the p variable to call the paint method on the PaintingCompany object like this:

p.paint("red")

This code will call the paint method on the PaintingCompany object, passing in the string "red" as the argument.

In the case when the company also wants to hire another service provider, DecoratingCompany. The DecoratingCompany can implement the Painter interface by providing an implementation for the paint method like this:

class DecoratingCompany(Painter):
def paint(self, color: str) -> None:
# implementation

Now, the company can hire the DecoratingCompany by creating a variable of the interface type Painter and assigning a value of type DecoratingCompany to it:

dc = DecoratingCompany()
d: Painter = dc

the company can now use the d variable to call the paint method on the DecoratingCompany service provider like this:

d.paint("blue")

In this way, the company can hire any service provider that satisfies the Painter interface in Python.

NodeJS, the dynamic and fast-paced employer

Node.js is like a dynamic and fast-paced employer that values innovation and adaptability. It is willing to hire candidates who are able to think on their feet and come up with creative solutions to problems.

For example in NodeJS. The company might define the Painter interface like this:

class Painter {
constructor() {
if (new.target === Painter) {
throw new TypeError("Cannot construct Abstract instances directly");
}
}

paint(color) {
throw new TypeError("Do not call abstract method paint from child.");
}
}

Here, the Painter interface is defined using a class with an abstract paint method. The constructor function throws an error if an instance of the Painter class is created directly, since the Painter class is meant to be an abstract base class. The paint method also throws an error if it is called directly from a child class, since it is meant to be implemented by child classes.

Now to hire a service provider named PaintingCompany that can paint buildings, The PaintingCompany can implement the Painter interface by providing an implementation for the paint method like this:

class PaintingCompany extends Painter {
constructor() {
super();
// fields
}

paint(color) {
// implementation
}
}

Here, the PaintingCompany class has a paint method with the correct signature to satisfy the Painter interface. It also specifies that it extends the Painter interface using the extends keyword.

Now, the company can hire the PaintingCompany by creating a variable of the interface type Painter and assigning a value of type PaintingCompany to it:

const pc = new PaintingCompany();
const p: Painter = pc;

Here, the variable p is of type Painter, and its value is an instance of the PaintingCompany class. Since the PaintingCompany class has specified that it extends the Painter interface, this assignment is valid.

The company can now use the p variable to call the paint method on the PaintingCompany service provider like this:

p.paint("red");

This code will call the paint method on the PaintingCompany object, passing in the string "red" as the argument.

In the case when hiring another service provider, DecoratingCompany, which can decorate buildings with wallpaper. The DecoratingCompany can implement the Painter interface by providing an implementation for the paint method like this:

class DecoratingCompany extends Painter {
constructor() {
super();
// fields
}

paint(color) {
// implementation
}
}

Now, the company can hire the DecoratingCompany by creating a variable of the interface type Painter and assigning a value of type DecoratingCompany to it:

const dc = new DecoratingCompany();
const d: Painter = dc;

The company can now use the d variable to call the paint method on the DecoratingCompany service provider like this:

d.paint("blue");

In this way, the company can hire any service provider that satisfies the Painter interface in NodeJS.

Just like in Python and Go, the company can hire any type that satisfies the Painter interface, regardless of whether the type explicitly declares that it implements the interface. This is in contrast to many other object-oriented languages, which require types to explicitly declare that they implement an interface.

Conclusion

In this analogy, Go, Java, Python, and Node.js are compared to different types of employers that have different hiring practices. Go is like a flexible and open-minded employer, Java is like a traditional and structured employer, Python is like a modern and progressive employer, and Node.js is like a dynamic and fast-paced employer. These differences can help developers understand the unique characteristics and approaches of each language when it comes to implementing interfaces and hiring service providers. Which one is your favorite? Please leave your comments below.

I hope this analogy has helped shed some light on the concept of interfaces in Go, Java, Python, and Node.js. As a beginner, I am always learning and trying to improve my understanding of different concepts of different languages. If you noticed any mistakes in this post, please don’t hesitate to let me know so that we can learn from each other and grow together. If you enjoyed this post and would like to stay updated on my future content, please give me a “CLAP” and follow me here and on Twitter. Thank you for reading!

--

--

DSL
DSL

Written by DSL

Sr software engineer. Love in Go, JavaScript, Python, and serverless AWS. Follow me for tech insights and experiences. follow me on twitter @terraformia

No responses yet