Piotr Gajek
written byPiotr Gajek
posted on August 10, 2022
Technical Architect and Full-stack Salesforce Developer. He started his adventure with Salesforce in 2017. Clean code lover and thoughtful solutions enthusiast.

Constants in Apex

Introduction

Picklist values, profile and permission set names, common business values - all constants can be scattered across Apex code.
The best practice is to store them in one place to avoid repetitions. (DRY)
The most common approach to resolve it is Constants class, which contains all final variables.

e.g

public class Constants {
    public static final String ACCOUNT_PROSPECT_TYPE = 'Prospect';
    public static final String ACCOUNT_CUSTOMER_DIRECT_TYPE = 'Customer - Channel';

    public static final String CONTACT_WEB_LEAD_SOURCE = 'Web';
    public static final String CONTACT_PHONE_INQUIRY_LEAD_SOURCE = 'Phone Inquiry';

    public static final String SYSTEM_ADMINISTRATOR_PROFILE = 'System Administrator';
    public static final String CUSTOMER_COMMUNITY_LOGIN_USER_PROFILE = 'Customer Community Login User';

    //etc.
}

BUT
What is the problem here?

  • All variables are initialized even if you don't need them - Apex Heap Size is reduced.
  • Violation of Single Responsibility Principle - Class contains no related constants.
  • Issues during development - Developer can override each other.
  • Pull Request issue - A lot of merge conflicts.
  • God class - has too many unrelated or uncategorized variables.
  • Class has a lot of lines of code - Hard to read, hard to navigate.

Solution

System.debug(Consts.ACCOUNT.TYPE.PROSPECT); // 'Prospect'
System.debug(Consts.ACCOUNT.RATING.HOT); // 'Hot'

System.debug(Consts.CONTACT.API_NAME); // 'Contact'
System.debug(Consts.CONTACT.LEAD_SOURCE.WEB); // 'Web'

System.debug(Consts.OPPORTUNITY.TYPE.EXISTING_CUSTOMER_DOWNGRADE); // 'Existing Customer - Downgrade'

Code

// ContactConsts.cls
public class ContactConsts {
    public static final ContactConsts INSTANCE = new ContactConsts();

    public final String API_NAME = Contact.sObjectType.getDescribe().getName();

    public final LeadSource LEAD_SOURCE = new LeadSource();

    private class LeadSource {
        public final String OTHER = 'Other';
        public final String PARTNER_REFERRAL = 'Partner Referral';
        public final String PHONE_INQUIRY = 'Phone Inquiry';
        public final String PURCHASED_LIST = 'Purchased List';
        public final String WEB = 'Web';
    }
}
// Consts.cls
public with sharing class Consts {
    public static final ContactConsts CONTACT {
        get {
            return ContactConsts.INSTANCE;
        }
    }
}

Description

  • All ConcreteConsts are singletons, so only one instance can be created.
  • The code in a get accessor executes when the property is read. It means that the class will be initialized on demand. Not all classes are initialized at once. It can save Apex Heap Size.
  • If a property has only a get accessor, it is considered read-only. The developer cannot set variable values.
  • Code is following Open/Close - easy to add new constants, without the need of change in existing code.
  • Code is following the Single Responsibility Principle - each class is responsible for Constants related to it.
  • Easy to read, understand and find the needed variable.
  • Easy to use - System.debug(Consts.CONTACT.LEAD_SOURCE.WEB); // 'Web'

Performance

I invoke standard and new approach in for (Integer i = 0; i < 1000; i++).

  • One - Standard
System.debug(ReferenceConsts.ACCOUNT_TYPE_CHANNEL_PARTNER_RESELLER);
  • One - Framework
System.debug(Consts.ACCOUNT.TYPE.CHANNEL_PARTNER_RESELLER);
  • Multiple - Standard
System.debug(ReferenceConsts.CONTACT_API_NAME);
System.debug(ReferenceConsts.ACCOUNT_TYPE_CHANNEL_PARTNER_RESELLER);
System.debug(ReferenceConsts.PROFILE_ANALYTICS_CLOUD_INTEGRATION_USER);
  • Multiple - Framework
System.debug(Consts.CONTACT.API_NAME);
System.debug(Consts.ACCOUNT.TYPE.CHANNEL_PARTNER_RESELLER);
System.debug(Consts.PROFILE.NAME.ANALYTICS_CLOUD_INTEGRATION_USER);
Property One - Standard One - Framework Multiple - Standard Multiple - Framework
CPU Time [ms] 23 19 31 38
Heap Size [bytes]* 1137 475 1137 1491

*Heap Size depends on the number of consts in class. The standard approach will always have the same heap size because all variables are initialized at once. Framework initializes const classes dynamically - more different const = more heap size in use.

As you can see there almost no difference with CPU Time.
Heap Size can be reduced when one type of Constants is used (eg. AccountConsts).

Repository

Github


If you have some questions feel free to ask in the comment section below. 🙂

Was it helpful? Check out our other great posts here.


Buy Me A Coffee