The ability to interact with an idea while safely ignoring some of its details. A set of operations on shared state that make solving problems easier. Examples:
communication: Abstractions (and as we will see, design patterns, implementation styles, architectural tactics …) are a communication mechanism and a shorthand.
sharedabstractions: allow for quicker and more effective understanding of the code.
naturalness: Some abstractions might come directly from the domain (e.g., a Customer class) (all of OO is arguably premised on this idea)
simplify: hide complexity and implementation details
interoperability: pass data around e.g. in JSON
All models are wrong; some are useful. (George Box)
Activity: List API
WriteGet LLM to write a function to reverse a list with element-wise operations
Node.getNext()
Node.setNext()
Activity
Now write the same function for a list with list operations
List.get(i)
List.set(i)
List.remove(i)
Activity
Finally, write the function with element operations
Node.getNext()
Node.setNext()
Node.getPrev()
Node.setPrev()
Abstractions in Each Example
For each solution, write out the key abstractions being used and how that impacts your solutions.
Write your names on the paper and hand it in so I can review.
depends on the abstraction and implementation (ArrayList vs LinkedList)
the interface List thus does not specify reverse() (why?)
Lists
Many different operations and syntactic formulations (list comprehensions, direct access, function access).
Like all abstractions, hides some irrelevant information from the user (in this case, how it is stored on disk).
Good abstraction leaves implementation details to talented developer (e.g. Java collections framework).
Example: Promises
Concurrency is a well known challenge for people to comprehend. One tricky part in Javascript (among others) has been the idea of callbacks. A callback is something like
And the advantage is it can execute asynchronously. But you can end up with long nested chains of callbacks, making this abstraction hard to parse. (why?)
Callback hell
doSomething(function (result) {doSomethingElse(result,function (newResult) {doThirdThing(newResult,function (finalResult) {console.log(`Got the final result: ${finalResult}`); }, failureCallback); }, failureCallback);}, failureCallback);
Example: Promises
A new abstraction called Promise was introduced to manage this chaining.
let myPromise =newPromise(function(myResolve, myReject) {// slow code (e.g. network access)myResolve();// when successfulmyReject();// when error});
And then the caller uses a Promise consumer:
myPromise.then(function(value) { /* code if successful */ },function(error) { /* code if some error */ });
and the promise knows to call the appropriate function.
Promise Advantages
The big advantage is chaining Promises, so we avoid deeply nested callback chains. Instead, we get some guarantees of how the Promises return, and can attach a single error handler.
Promises
doSomething().then((result) =>doSomethingElse(result)).then((newResult) =>doThirdThing(newResult)).then((finalResult) => {console.log(`Got the final result: ${finalResult}`); }).catch(failureCallback);
There are more reasons to use Promises, and their evolution, async/await, but this covers the abstraction and what it enables.
Note the development here: blocking functions –> asynchronous callbacks –> Promises –> Async/Await –> …
Good Abstractions
Should do one thing and do it well
If hard to name, that’s a bad sign
Implementation should not leak into abstraction
If there’s details that do not need to be exposed, do not
Names matter
Be self-explanatory, consistent, regular
Challenges
What operations to include? (a.ka., interface)
Choices of operations has many consequences
How to version your abstraction? Abstractions get stale.
How does your abstraction fit with others?
Caveat: Leaky Abstractions
Think about our List example. Where could our abstraction get us into trouble?
A leaky abstraction is an abstraction in which the hidden details can leak out of the abstraction boundaries. For example, thinking of time as a single, 24h cycle without understanding time zones or leap seconds. Or not understanding that the world isn’t ASCII-based.
Summary
Good design is about abstraction:
when to use them, what to call them, how to compose them
abstractions allow designers to ignore implementation specifics