4.4.1.3–4.4.1.8 - Abstraction and information hiding
Learn how useful models retain essential detail, how interfaces hide implementation, and how a complicated problem can become a familiar solvable one.
Selecting the detail a model needs
A computer cannot work directly with every detail of a real situation, so we choose a representation that contains the information needed for a particular purpose. Representational abstraction removes unnecessary details from that representation. The purpose determines what is unnecessary.
For a journey planner, a railway network can be represented by stations and connections, with travel times attached to the connections. A photograph of each platform, the colour of its benches and the positions of nearby trees add little to finding the fastest route. The planner must retain a connection's travel time and whether the connection exists. If accessibility is a requirement, the presence of lifts may also become essential.
Representational abstraction
A representation obtained by removing details that are unnecessary for the problem being solved.
Abstraction is not permission to remove inconvenient requirements. A straight-line map that ignores one-way streets is unsuitable for a road route planner even if it looks simpler. A useful abstraction is simpler and still adequate for its stated purpose. The same object can have different abstractions: an architect needs platform dimensions; a traveller may need departure times.
Grouping by common characteristics
Abstraction by generalisation or categorisation identifies common characteristics and groups objects in a hierarchy. The relationship is “is a kind of”. It describes what a thing is, rather than simply which details a representation leaves out.
For example, a bus and a train can both be categorised as kinds of public transport. Public transport is a kind of transport. A bus may have the shared transport characteristics of carrying passengers and having a journey time while retaining its particular road-based characteristics. We can reason about the common category before considering each special case.
A hierarchy might be read as “electric bus is a kind of bus; bus is a kind of public transport; public transport is a kind of transport”. Every lower category must satisfy the defining characteristics of the category above it. “A bus contains seats” is a has-a relationship, so it does not make seats a kind of bus. Likewise, grouping vehicles by today's location is not automatically a meaningful type hierarchy.
The two forms of abstraction can work together. A journey planner can represent each vehicle by a small set of relevant properties, then group buses and trains under public transport so that shared reasoning applies to both. Removing a photograph is representational abstraction; identifying a common transport category is generalisation.
Exposing an interface and hiding details
Information hiding makes essential characteristics available while concealing details that a user of an object does not need. In software, an interface states the operations a component provides, their inputs and the results or effects a caller may rely on. Its implementation is how those operations are carried out.
A ticket machine exposes an operation for buying a ticket. The customer needs to know destinations, prices, payment requirements and what ticket will be issued. The customer does not need to know the internal storage layout or how the printer motor is controlled. Those details are hidden, while the essential behaviour remains available.
This separation also helps software clients. If a component changes its internal representation but keeps the promised interface behaviour, callers can continue using it. However, hiding detail does not mean leaving the interface vague. Preconditions, such as requiring a valid destination, and outcomes, such as issuing one paid ticket, are part of the behaviour a caller needs to understand.
Representational abstraction concerns the model we choose; information hiding concerns the details available to a user of an abstraction. They are closely related, but “the model has fewer fields” and “clients cannot depend on its internal fields” make different claims.
Separating a method from a function
Procedural abstraction removes the particular values in an individual computation to expose a reusable computational method. Suppose we total the four prices 3, 5, 2 and 6 by visiting each price in turn and adding it to a running total. Abstracting away those particular prices gives the method “visit each supplied price and accumulate the sum”. Different input values can use the same procedure.
Procedural abstraction
An abstraction representing a computational method, obtained by disregarding the particular values used in a computation.
Functional abstraction makes a further abstraction: it disregards the particular method and describes the mapping from inputs to a result. A sum function maps a finite collection of numbers to their total. Its user can rely on that input–result relationship without needing to know whether the implementation accumulates left to right, combines groups of values or uses another correct method.
In the specification's distinction, procedural abstraction yields a procedure or method, not yet a function. Functional abstraction hides the method as well. A function here is an abstract input–result mapping; the fact that a programming language happens to call a method a “function” does not remove this conceptual distinction.
For the same example, “replace the four fixed prices with supplied prices but retain accumulation” is procedural abstraction. “Specify the total produced for any allowed input, without specifying how it is calculated” is functional abstraction. Hiding the method still requires an exact contract: a total of no prices is zero, and rounding rules would need stating if they mattered.
Using compound data through operations
Data abstraction separates how a compound data object is used from how it is constructed and represented. It allows us to define a new kind of object using existing data types while offering operations appropriate to that new object.
A stack stores items in last-in, first-out order. A client can push an item, pop the most recently pushed remaining item, and ask whether the stack is empty. An implementation might use an array of items and an integer pointing to the top. The array and integer are existing types from which the compound stack is built.
The client needs the stack behaviour, including what happens on an attempted pop from an empty stack. It need not manipulate array indices or the top pointer itself. Another implementation could use linked nodes and still support the same operations. If clients depend only on the stack interface and contract, this change does not alter their use of the stack.
This is more than disguising a variable name. The abstraction defines meaningful operations and protects the relationship between the component values. Allowing every client to alter the top pointer directly would let clients violate the stack's representation rules, weakening the abstraction.
Reducing a problem to a known solution
Problem abstraction or reduction removes irrelevant detail until a problem can be represented as one for which a solution is already known. We must preserve enough structure that a solution to the simpler representation answers the original question.
Imagine finding a route using the fewest direct station-to-station connections. The physical appearance of the stations and road surfaces can be discarded. Represent stations as vertices and usable direct connections as edges. If each edge represents one equal-cost step, the problem becomes finding a shortest path in an unweighted graph, for which an established search method exists. The resulting sequence of vertices is then translated back into station names. Counting vehicle changes would need extra information about which services operate the connections.
If the original requirement is minimum travel time instead, unequal journey times cannot be discarded. They must become edge weights, and a suitable weighted shortest-path method is needed. Reduction therefore involves identifying the right known problem, not merely a superficially similar one.
Reduction differs from decomposition. Decomposition splits a task into smaller tasks; reduction recognises a known problem inside a suitable representation. The two may be used together, but splitting “route planning” into screens and payment processing does not by itself reduce the route problem to shortest path.
A useful abstraction retains the relationships and constraints needed to turn its answer back into a correct answer to the original problem.