What is an Interface?
Software (and all technology) is just tools connected to other tools. If you look (or mentally zoom) into a tool, it’s made up of even more tools that connect to each other! Whether hardware or software: a tool connects (or interacts) with another through its interface.
Tools can be found in all shapes and sizes — and so can interfaces. Let’s branch out this discussion into levels of scope to distinguish between different ‘sizes’ of interfaces. The lower the scope, the more zoomed in we are:
- Low Level: Interfaces of tools in the same application
- Mid Level: Interfaces of tools in the same machine
- High Level: Interfaces of tools across different machines
Note: some terms help paint a picture in the reader’s mind and should not be interpreted literally. One example is ‘sizes’ of interfaces - no one ever refers to the ‘size’ of an interface this way, but it does a great job of painting a picture of zooming into a machine. Terms like this will be ‘quoted’.
Interfaces provide points of connection (or doors) through encapsulated software. The walls of encapsulation come in varying ‘strengths’ which determine ease of access to the code or data behind the interface without actually using the interface. The ‘encapsulation strength’ associated with these interfaces is something we’ll track throughout this post.
Table of Contents
Even in the scope of inside an application, we can branch out our descriptions into three finer levels of scope:
In modern programming languages, functions are the smallest tool which encapsulate behavior while providing an interface (for that boxed behavior). A function’s signature is its interface. It’s the first line of code in a function’s declaration which defines the inputs and outputs of the function. It is—at-a-minimum—the parameters accepted by the function as input.
Because of the fundamental differences between statically and dynamically typed languages, a programming language’s function signature may or may not include the type of the parameters. Here’s an example in a few languages:
# Python is a dynamically typed language, so you don't # specify the types of variables. def my_function(arg1, arg2): my_variable = 0 # Process stuff using arg1, arg2 return my_variable result = my_function(False, -1) # Do stuff with result
Functions provide relatively weak encapsulation because a function has access to any variable defined in its parent scope. This makes it very easy for functions to become tightly coupled with their parent scope. Let’s see using an example:
parent_scope_var = "function's parent scope" def my_function(): parent_scope_var = "function's scope" my_function() # Will print: function's scope print(parent_scope_var)
Classes and structs are two alternative tools for encapsulating functions, primitive data types, and other classes/structs. When a class or struct is composed of these nested elements, the elements are called member variables. The exact differences between structs and classes vary depending on the language. Most languages only have one or the other (C++ and Swift have both). Member variables are typically accessed by prefixing the name of the class/struct when calling a class/struct’s member variable.
A class or struct’s public members are its interface. However, most modern languages allow you to define a class/struct’s interface separately from the actual struct/class. This feature enables more polymorphism in strictly typed languages because it allows a variable to represent any class that implements that interface. Let’s see examples:
# Python does not have an `interface` type, but thanks to # its dynamic typing, you you can use a variable to # represent any classes that share function names: class MyObject: def do_something(self): # Do something class MyOtherObject: def do_something(self): # Do something for this_object in (MyObject(), MyOtherObject()): this_object.do_something()
Libraries are collections of related functions and classes/structs. A library’s interface is the collection of public/exported functions and classes/structs. If a library is generic enough to be used by many components, its interface is known as an API (Application Programming Interface). An introductory post about library APIs can be found here.
Programming languages typically provide features to modularize groups of functions, classes/structs, or files into ‘bigger boxes’. These logical barriers prevent variable names from clashing, and can even prevent access to functions, variables, and classes/structs which should only be used by the library. Here are the names of the tools used by a few programming languages to encapsulate at the Library scope:
- Python: Module
- Java: Package
- PHP: Namespace
- Golang: Package
Providing concrete examples of what an interface looks like between applications in a machine is difficult because of the number of:
- Layers of applications that can exist between a modern application and the operating system’s Kernel.
- Different languages we could have used to write either application.
- Different tools we could have used to facilitate this interaction.
- Different mechanisms offered by the operating system to facilitate interaction between processes.
However, in all cases, communication between processes is called IPC (Inter-Process Communication). IPC is also used to describe communication between machines, since applications on different machines will obviously be running on different processes - but it more commonly refers to processes in the same machine. An important point to note is that there’s not always a 1-to-1 relationship between an application and a process. One application can spawn multiple processes while each one of those processes can execute multiple applications. Another important note is that in all circumstances, an application must directly or indirectly interact with the Kernel in order to facilitate any form of communication with another process.
Here are some links for more info:
- How a compiled program interacts with an operating system, and a system's hardware — read the comments too.
- Wikipedia has a list of various approaches to IPC.
- GeeksForGeeks has a post about two approaches IPC (shared memory and message queues).
- Advanced post on how piping between two processes works.
Very strong logical barriers exist between processes since all communication between processes goes through the Kernel.
Most approaches of communication between processes in one machine can be reconfigured for communication with another machine over a network. Network sockets require relatively minimal reconfiguration since a machine’s internal loopback connections are treated just as they would be from another machine. Network sockets are the lower-level which enable Web APIs to interact with one another. An intro to Web APIs can be found here.
Very strong ‘barriers exist’ since the processes are on different physical machines.
There are endless more flavors of interfaces because of the broad scope of the word. Many of them don’t fall under the vertical levels of scope outlined here. For example, a developer’s naming convention when writing code is a sort of interface. This is because you can think of one developer’s variable names as an interface to communicate their intention to the next developer who comes across that code. When you think of interfaces in such a broad scope, you start seeing them everywhere. Here are some important interfaces that did not fit in this post’s levels of scope:
User-interfaces are how people interface with machines. You’re using one right now to view this blog. In fact, since your user-interface is able to display images and shapes, it’s a graphical user-interface (GUI).
URLs are both a user-interface and machine-interface. It helps people because it hides the fact that every URL points to an IP. It also helps Google’s bots make sense of a web page. When query parameters are provided, URLs feel a lot like a public function’s interface.CLIs, as well, are both a user-interface and a machine-interface. A CLI is the interface for a type of program called a `shell`. Nearly everyone has seen these when booting up a computer (Mac computers don't display it). Applications within the same machine commonly use a shell as a middle-man (or *glue*) to execute another application.
This post attempted to break down software interfaces into vertical levels of scope, and provide a bird’s eye view of this broad term. Hopefully, it’s helpful.