Embracing SOLID Principles in Apex: A Guide for Salesforce Developers
Solid Principles

Embracing SOLID Principles in Apex: A Guide for Salesforce Developers

Salesforce development often involves creating robust and scalable solutions to meet the dynamic needs of businesses. To achieve this, developers can turn to SOLID principles — a set of design principles that promote maintainability, flexibility, and scalability in object-oriented programming. In this blog post, we’ll explore how Salesforce developers can apply SOLID principles in Apex.

S- Single Responsibility Principle (SRP):

The Single Responsibility Principle states that "A module should be responsible to one, and only one, actor. In simpler words, class should have only one reason to change. In Apex, this means each class should focus on a single task.

Business Case: Assume you are building a Salesforce application for a financial institution. You have a class responsible for handling both customer data validation and database interactions.

// Before applying SRP
public class CustomerHandler {
    public Boolean validateCustomerData(Account customer) {
        // validation logic
    }

    public void saveCustomerToDatabase(Account customer) {
        // database interaction logic
    }
}        

After applying SRP:

Separate these responsibilities into two classes:

// After applying SRP
public class CustomerValidator {
    public Boolean validateCustomerData(Account customer) {
        // validation logic
    }
}

public class CustomerDatabaseHandler {
    public void saveCustomerToDatabase(Account customer) {
        // database interaction logic
    }
}        

By doing this, each class has a single responsibility, making the code more maintainable and allowing for easier updates.

O- Open/Closed Principle (OCP):

The Open/Closed Principle encourages developers to design classes in a way that allows extension without modifying existing code. In Apex, we can achieve this through interfaces and abstract classes.

Business Case: Imagine you are developing a Salesforce application for inventory management. Initially, you have a class handling product pricing, but you want to extend it without modifying the existing code.

// Before applying OCP
public class ProductPricer {
    public Decimal calculatePrice(Product product) {
        // pricing logic
    }
}        

After applying OCP:

Introduce an interface and create specific implementations:

// After applying OCP
public interface PriceCalculator {
    Decimal calculatePrice(Product product);
}

public class StandardPriceCalculator implements PriceCalculator {
    public Decimal calculatePrice(Product product) {
        // standard pricing logic
    }
}

public class DiscountedPriceCalculator implements PriceCalculator {
    public Decimal calculatePrice(Product product) {
        // discounted pricing logic
    }
}        

Now, new pricing strategies can be added without modifying the existing .

L- Liskov Substitution Principle (LSP):

The Liskov Substitution Principle states that objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program. In Apex, this means that subclasses should extend the behaviour of their parent class without altering it.

Business Case: In your Salesforce project for a healthcare provider, you have a class hierarchy for managing patient records. Ensure that subclasses can be used interchangeably without affecting the functionality.

// Before applying LSP
public class PatientRecord {
    public String generateReport() {
        // common logic for generating a patient report
    }
}

public class PediatricPatientRecord extends PatientRecord {
    public String generateReport() {
        // logic for generating a pediatric patient report
    }
}        

After applying LSP:

Use an abstract class to represent the common behavior:

// After applying LSP
public abstract class PatientRecord {
    public abstract String generateReport();
}

public class PediatricPatientRecord extends PatientRecord {
    public String generateReport() {
        // logic for generating a pediatric patient report
    }
}        

Now, subclasses can be substituted without affecting the behaviour of the parent class.

I- Interface Segregation Principle (ISP):

The Interface Segregation Principle suggests that a class should not be forced to implement interfaces it does not use. In Apex, this means breaking down large interfaces into smaller, more specific ones.

Business Case: Consider a scenario in a Salesforce project where you are implementing a system for tracking customer interactions. You have a comprehensive logging interface, but not all classes need to implement all methods.

// Before applying ISP
public interface CustomerInteractionLogger {
    void logInfo(String message);
    void logError(String message);
    void logDebug(String message);
}        

After applying ISP:

Split the interface into smaller, more specific interfaces:

// After applying ISP
public interface InfoLogger {
    void logInfo(String message);
}

public interface ErrorLogger {
    void logError(String message);
}

public interface DebugLogger {
    void logDebug(String message);
}        

Now, classes can implement only the interfaces relevant to their needs, avoiding unnecessary dependencies.

D- Dependency Inversion Principle (DIP):

This principle states that “ High-level modules should not import anything from low-level modules.” Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.

In simpler terms , “Dependency Inversion” is the strategy of depending upon interfaces or abstract functions and classes rather than upon concrete functions and classes. Simply put, when components of our system have dependencies, we don’t want directly inject a component’s dependency into another.

Business Case: In your Salesforce project, you have a class that directly depends on a specific external service for data retrieval, violating the Dependency Inversion Principle.

// Before applying DIP
public class DataService {
    private ExternalApiService apiService;

    public DataService() {
        this.apiService = new ExternalApiService();
    }

    public List<Account> getAccounts() {
        // using the external service to retrieve accounts
        return apiService.retrieveAccounts();
    }
}        

After applying DIP:

Introduce an interface for the external service and use dependency injection to pass the service into the class.

// After applying DIP
public interface DataRetrievalService {
    List<Account> retrieveAccounts();
}

public class ExternalApiService implements DataRetrievalService {
    public List<Account> retrieveAccounts() {
        // implementation for retrieving accounts
    }
}

public class DataService {
    private DataRetrievalService dataRetrievalService;

    public DataService(DataRetrievalService dataRetrievalService) {
        this.dataRetrievalService = dataRetrievalService;
    }

    public List<Account> getAccounts() {
        // using the injected service to retrieve accounts
        return dataRetrievalService.retrieveAccounts();
    }
}        

By applying the Dependency Inversion Principle, the DataService class is no longer tightly coupled to a specific implementation of the data retrieval service. This makes it easier to replace or update the external service without modifying the DataService class, promoting flexibility and maintainability in the Salesforce codebase.

Thanks for reading!

#salesforce #salesforcedevelopers #programming


Great insights on applying SOLID principles in Apex development – a crucial aspect for building robust and scalable Salesforce solutions. Looking forward to delving deeper into this topic!

要查看或添加评论,请登录

Deep B.的更多文章

  • Iterable Batch Apex

    Iterable Batch Apex

    Let’s traverse through data! The Database.Batchable interface contains three methods that must be implemented.

  • Efficient SOQL queries

    Efficient SOQL queries

    If you’re doing development in an org with a Large Data Volume then you must be careful with SOQL For best performance,…

  • Dive into : Database.Stateful

    Dive into : Database.Stateful

    We know that in Salesforce we have a powerful way to execute things in bulk using Batch Apex . But seldom we arrive in…

  • Notable Nuances in JavaScript- 01

    Notable Nuances in JavaScript- 01

    This short read highlights some features that may not be majorly used 1. Replace can accept a callback function The…

    2 条评论

社区洞察

其他会员也浏览了