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 we’re zoomed in:
- 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: these are not related to programming language levels of scope (high vs low level languages), but the concept of “levels of scope” is the same.
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
Low Level: Same Application
Low->Mid: Classes and Structs
Mid Level: Same Machine
High Level: Different Machines
Even in the scope of inside an application, we can branch out our descriptions into three finer levels of scope:
Functions are the smallest software component which encapsulate code while providing an interface for interaction. A function’s signature is its interface. A function’s signature is typically the first line of code in a function’s declaration which defines the inputs and outputs of the function. It is the sum of:
- the arguments accepted by the function as input
- the type of data returned by the output of the function
Because of the fundamental differences between statically and dynamically typed languages, a programming language’s function signatures may or may not include the type of the arguments. Statically typed languages require defining the types for arguments on input and output while dynamically typed languages do not. Here are some function declarations with their signatures:
# 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
Note: functions, arguments, and variables should always have descriptive names.
Functions provide a relatively weak type of encapsulation because they have access to any variable defined in their 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 ways of 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 accessed by prefixing the name of the class/struct when calling a class/struct’s function or other 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 (very nifty). 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 is here: What is an API?.
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.
- 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 our application and the Kernel
- different languages we could have used to write either application
- different libraries 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.
IPC deserves its own post, so I’ll leave you with this simplified diagram of one type of IPC (called piping). All forms of IPC go through some sub-component of the Kernel:
And these links for more info:
- Wikipedia has a nice list of various approaches to IPC.
- GeeksForGeeks has a nice post on two of the approaches of IPC shared memory and message queues.]
- Advanced post on how piping works.
Very strong logical barriers exist between processes since all communication between processes goes through the Kernel. On top of that, preventing processes from reading another process’s memory is one of the Kernel’s most critical duties.
Most approaches of communication between processes in one machine can be reconfigured for communication with another machine over a network. Network Sockets require virtually zero reconfiguration since a machine’s internal loopback connections are treated just as they were be from another machine. Network sockets are the lower-level tool that power high-level Web APIs to interact with one another. An intro to Web APIs can be found here: What is an API?.
Very strong physical 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 type of interface. You can think of one developer’s naming of variables in the code they write is an interface to communicate their intention to the next developer who comes across that code. When you think of interfaces with that much of 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 the most common way for people to interface with machines. You’re using one right now to view this blog.
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 act a lot like public functions.
CLIs are also both a user-interface and 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. If you know of another way we could break down this loaded term, or if you know of another type of interface worth mentioning, please let me know!
Questions and suggestions are always welcome.