What is an Interface?
March 05, 2020
Difficulty: Beginner
Overview
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
Examples
Low Level: Same Application
Even in the scope of inside an application, we can branch out our descriptions into three finer levels of scope:
- Low->Low Level: Functions
- Low->Mid Level: Classes & Structs
- Low->High Level: Libraries
Low->Low Level: Functions
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 Python:
# 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: Encapsulation Strength -- Weak
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)
Low->Mid Level: Classes & Structs
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()
// Go (language)
type MyInterface interface {
DoSomething()
}
type MyStruct struct {}
type MyOtherStruct struct {}
func DoSomethingImportant(someObject MyInterface) {
// someObject can be either MyStruct or MyOtherStruct
someObject.DoSomething()
}
func (myStructVar MyStruct)DoSomething() {
// Do something
}
func (myOtherStructVar MyStruct)DoSomething() {
// Do something
}
Classes and Structs: Encapsulation Strength -- Moderate to Strong
The 'encapsulation strength' of classes and structs depends on the language. Languages typically provide access modifiers for restricting access to the variables held by classes and structs. Python is one language that doesn't have any access modifiers. Golang and JavaScript do have access modifiers, but they operate at the Library scope, restricting or exposing access to variables between packages (Golang) or modules (JavaScript).
Low->High Level: Libraries
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.
Libraries: Encapsulation Strength -- Typically Strong
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
- JavaScript: Module
- Java: Package
- PHP: Namespace
- Golang: Package
JavaScript and Golang implement access modifiers at this level, rather than at the function or class/struct level.
Mid Level: Same Machine
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.
Inter-Process: Encapsulation Strength -- Very Strong
Very strong logical barriers exist between processes since all communication between processes goes through the Kernel.
High Level: Different Machines
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.
Different Machines: Encapsulation Strength -- Very Strong
Very strong 'barriers exist' since the processes are on different physical machines.
Other Interfaces
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-Interface
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
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.
Command-Line Interface (CLI)
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.
Final Words
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.