AbstractionReading Only
Imagine you're a devoted fan of a fictional book series. If you were to tell me about the series, you'd focus on the essential elements. You'd provide a brief overview of the major characters, the central conflict, and the overarching themes. However, you wouldn't delve into the subplots, minor characters, or intricate details.
If you were to tell me about a particular book within the series, you'd summarize the key events, character development, and significant plot twists. You'd still leave out minor details and any irrelevant subplots.
If you were to tell me about a particular chapter within a particular book, you'd include the important scenes, character interactions, and even dialogue.
At each level, you have a goal in mind. You purposefully exclude unimportant details to emphasize the ones that truly matter. By managing the level of detail based on relevance, you effectively control complexity. This concept, when applied to software development, is known as Abstraction.
Building "quality" software, characterized by modular, extensible, and reusable code, reduces complexity by promoting simplicity. Complexity decreases when we include fewer intricate details. To amplify the essential details, we must find the right level of abstraction by deciding which details to include.
The world of abstraction is quite nuanced, and you could spend weeks studying it and the different opinions out there. For now, let's just provide you with a brief overview of what is commonly accepted as abstraction:
At the lowest level of abstraction, you have the most detail - a single method in isolation:
public Decimal calculateTotalRevenue(Account acc){ Decimal totalRevenue = 0; // calculation return totalRevenue; }
You've effectively abstracted out the total revenue calculation. You've performed anti-unification. I wouldn't necessarily call this method itself an abstraction, but rather, it represents the idea of an abstraction.
Go one level higher. Say you need to calculate total revenue differently based on the account's record type. You can abstract the complexity by using inheritance:
public interface TotalRevenueCalcStrategy { Decimal calculateTotalRevenue(Account a); } // For Accounts with 'Partner' record type public class PartnerTotalRevenueCalc implements TotalRevenueCalcStrategy { public Decimal calculateTotalRevenue(Account a){ Decimal totalRevenue = 0; // partner implementation return totalRevenue; } } // For Accounts with 'Direct' record type public class DirectTotalRevenueCalc implements TotalRevenueCalcStrategy { public Decimal calculateTotalRevenue(Account a){ Decimal totalRevenue = 0; // direct implementation return totalRevenue; }
You'll see the benefits of the abstraction when you see the caller of the code:
public class AccountController { @AuraEnabled(cacheable=true) public Decimal calculateTotalRevenue(Account a){ TotalRevenueCalcStrategy strategy = getStrategyBasedOnRecordType(a); return strategy.calculateTotalRevenue(a); } }
Don't worry about the unfamiliar syntax; we'll cover that in the Paradigm Features course. Instead, focus on what the abstraction is conveying here.
As you go through this code, you'll notice that it computes the total revenue for an account depending on its record type. It's reasonable to assume that there's a distinct formula for each record type. If you're interested in understanding the specifics of the formula or how it selects the strategy, you can open up these methods and dive deeper. If that's not what you care about, you can proceed to review other, more relevant pieces of code. That's the value abstraction adds.
If you go even one level higher, say to the LWC calling this code, you'd see another view being abstracted:
import { LightningElement, track } from "lwc"; import calculateTotalRevenue from "@salesforce/apex/AccountController.calculateTotalRevenue"; export default class AccountLWC extends LightningElement { @track totalRevenue; @track error; handleLoad() { calculateTotalRevenue() .then((result) => { this.totalRevenue = result; }) .catch((error) => { this.error = error; }); } }
Here, we know very little about the underlying details. We just know there is a component that uses Apex to calculate the total revenue. If we care to find out more, we can dive deeper. Otherwise, we can read the code we're interested in.
Abstraction can be a broad term, and if you scour the internet, you'll see a lot of different opinions. I think it's because abstraction in the code doesn't physically exist; it's an incorporeal part of the codebase. ¯\_(ツ)_/¯