Showing posts with label interface. Show all posts
Showing posts with label interface. Show all posts

Sunday, 9 January 2022

ApexTypeImplementor in Spring 22



Note: This feature may be in Beta come Spring 22 - the release notes in Salesforce Help say that it is in beta, the PDF version doesn't mention it. If I get confirmation either way I'll update this post! Remember that Spring 22 is still in preview, so this feature might never see the light of day anyway!

Introduction

The Spring 22 release of Salesforce introduces the ApexTypeImplementor object, which contains information about the classes that implement an interface, either directly or indirectly. You've been able to access Apex class bodies for years, so picking up the direct implementation of an interface has been possible for a while. Figuring out that that the interface is implemented in the inheritance hierarchy gets a bit more tricky. In either case, having the platform take care of it for you takes away a chunk of code, which is always a good thing.

Use Cases

When I first came across this new feature it had a touch of the "solution looking for a problem" about it, but once I had a chance to dig into it in a little more detail, I have a number of scenarios where I'll be able to apply it as soon as it is available/maybe out of beta.

Rule Engine

The most common use case from my perspective is the configurable plug and play engine. I have several rules engines where a class is considered a rule because it implements a specific interface. I'll have a number of classes that implement rule variants, and these rules are enabled and applied in a specific order based on configuration. 

The ApexTypeImplementor doesn't help in terms of which rule classes should be applied, as I still want it to be driven through configuration rather than the code just applying everything it can find in whatever order they come back. Where it does help is to assist the admin who is configuring the rules. Rather than them having to remember the names of classes or look them up and copy/paste from other setup pages, I can create a custom configuration page that offers them only the classes that implement the interface, thus ensuring that the configured classes are available.

Setup Verification 

A less obvious use case is confirmation that a system is setup and configured correctly - i.e. that there is no missing or obviously broken configuration that will cause errors at runtime. Each feature that can be enabled has an associated class that checks the feature is configured correctly (or at least plausibly as it's quite hard to confirm correctness without running a bunch of tests). As long as each of these classes implements the same interface, I can execute code from an admin clicking a button or quick action that finds all of the classes that implement the verification interface. Each of these is instantiated and the method that checks the configuration is executed. This works really well if the additional features are delivered via org dependent or unlocked packages, as the code that implements the feature and the code that checks it are developed in tandem.

Training Confirmation

The third use case that I have is around training, and ensuring that the training has been retained. I have a collection of packages that I can drop into an org that ask the user to carry out some configuration or regular business work, and then check that they carried it out correctly. 

At the moment the packages create configuration entries when they are installed, and those configuration entries are used to generate cards on an application page so that users can access and check the challenges. Because I want all of the challenges that are installed to be available to the user, with the ApexTypeImplementor I can do away with the configuration entries and just show the users details of all the classes that implement the interface. 

Sample App

I've created a sample application in my Spring 22 repository to show ApexTypeImplementor used in anger. I have an interface that a class implements to describe itself to interested parties :

public interface ClassInfoIF 
{
    String getDescription();
    String getAuthor();
}

I'm using a Lightning Web Component to display the details of the classes, which extracts them from a dedicated controller (that also implements the interface!) : 

@AuraEnabled(cacheable=true)
public static List<ClassDetail> GetClassDetails()
{
    List<ClassDetail> classDetails=new List<ClassDetail>();
    List<ApexTypeImplementor> classInfoTypes = 
           [SELECT ApexClassId, ClassName, ClassNamespacePrefix
            FROM ApexTypeImplementor
            WHERE InterfaceName = 'ClassInfoIF' and IsConcrete=true];

    for (ApexTypeImplementor classInfoType : classInfoTypes)
    {
        ClassDetail classDetail=new ClassDetail();
        classDetail.classId=classInfoType.ApexClassId;
        classDetail.fullName='';
        if (null!=classInfoType.ClassNamespacePrefix)
        {
            classDetail.fullName=classInfoType.ClassNamespacePrefix + '.';
        }

        classDetail.fullName+=classInfoType.ClassName;
        ClassInfoIF classInfo = 
                    (ClassInfoIF) Type.forName(classInfoType.ClassNamespacePrefix,
                                            classInfoType.ClassName).newInstance();

        classDetail.description=classInfo.getDescription();
        classDetail.author=classInfo.getAuthor();
        classDetails.add(classDetail);
    }

    return classDetails;
}

The bolded sections above show the query that extracts the classes that implement the interface, and the code that constructs the class as an instance of ClassInfoIF and executes the methods that describe the class. If a new class is added to the org that implements this interface, it is automatically included the next time I access the page.

I've put in a few fake classes too, including one that implements the interface by overriding methods from its superclass to check the indirect side of things works too. Accessing the application page shows the information I'm interested in for each class:


Related

Saturday, 21 October 2017

Programming against Apex Interfaces

Programming against Apex Interfaces

Interfaces

Introduction

In the dim and distant past (getting on for 10 years ago now), when I was a Java programmer working in the systems integration and financial services space, the majority of the programming that I did was against interfaces rather than concrete class instances. This isn’t something that I see very often in the Salesforce world (at least on the SI side - I’d imagine ISVs go in for it a lot more) for a variety of reasons including:

  • Lots of Salesforce work is a point solution on an existing implementation, so it doesn’t always merit abstraction via interfaces.
  • Customers don’t want to the extra cost and accept that additional development effort may be required in future.
  • Many people have ended up as Salesforce developers through non-traditional routes and haven’t been exposed to interfaces.

Simply put, programming against interfaces means that rather than identifying the specific class that carries out an operation in your code, you identify an interface and rely on there being a class that implements that interface at run time. This allows your code to focus on what the operation needs to do rather than how it is accomplished.

Why?

Programming against interfaces introduces flexibility. You can change the implementation of the interface without affecting any of the code that uses the interface. Thus you could start out with a faker implementation that simply returns canned data to allow you to develop the real world interface implementation and the consuming code in parallel. It also means you can have multiple implementations of an interface inside a single system and swap them around via configuration.

Code me. Now.

The scenario for the example is calculating the discount due to an account. The customer has told us that at the moment it is a flat 10%, but this is an area that they will want to change in the future to take into account the account’s industry. This is a classic use case for an interface as we know that we will need to swap out the implementation in the future, and if it changes once it is like to change again in the future.

Concrete classes

My initial implementation of the class to calculate the discount is as follows:

public class SimpleAccountDiscount {
	public double getDiscount(Id accountId) {
        return 10;
    }
}

and I can use this directly in code:

double discount=new SimpleAccountDiscount().getDiscount('00124000004N1TfAAK');
System.debug('Discount = ' + discount);

producing the following output:

07:02:45:045 USER_DEBUG [2]|DEBUG|Discount = 10.0

all well and good. Next I create the more complex version which takes the industry into account - yes, complex is probably over-egging it a bit, but all things are relative.

public class ComplexAccountDiscount {
    public double getDiscount(Id accountId) {
        // default value
        Double discount=10;
        Account acc=[select id, Industry
                     from Account
                     where id=:accountId];

        if (acc.Industry=='Apparel’) {
            discount=15;
        }
        else if (acc.Industry=='Consulting’) {
            discount=5;
        }
        
        return discount;
    }
}

And I can use this in code just as easily :

System.debug('Discount for Burlington (apparel) = '
             + new ComplexAccountDiscount().getDiscount('00124000004RIGFAA4'));

System.debug('Discount for Dickenson (consulting) = '
             + new ComplexAccountDiscount().getDiscount('00124000004RIGHAA4'));

produces the output:

17:14:25:078 USER_DEBUG [1]|DEBUG|Discount for Burlington (apparel) = 15.0
17:14:25:081 USER_DEBUG [4]|DEBUG|Discount for Dickenson (consulting) = 5.0

However, when the customer is ready to move to the more complex version, I need to carry out a deployment in order to start using my new class. Plus if it turned out that a downstream system wasn’t ready for the change, I’d have to carry out another deployment to revert to the simple version. 

Implementing an interface

The following Apex interface reflects the method that must be exposed by any discount implementation:

public interface AccountDiscountInterface
{
    double GetDiscount(Id accountId);
}

Note that I don’t specify an access modifier for the method - as an interface reflects the public interface, the methods in the interface are implicitly public. I then modify may classes (only the complex version shown):

public class ComplexAccountDiscount implements AccountDiscountInterface {

I can then use the interface in place of a concrete class:

AccountDiscountInterface adi=new ComplexAccountDiscount();
System.debug('Discount for Burlington (apparel) = '
             + adi.GetDiscount('00124000004RIGFAA4'));

So my code that gets the discount doesn’t care about the implementation, but the previous line that instantiates the concrete class does. A partial success at most.

Dynamically instantiating a class

The final piece of the puzzle is dynamically instantiating a class based on configuration. If I can do this, my customer can switch implementations simply by changing a custom setting. Dynamical instantiation consists of two parts. First Type.forName() is used to get the type of the Apex class. Then the newInstance() method of the resulting Type is executed to create an instance of the named class. I’ve placed the name of the implementing class into an instance of the Account_Discount_Setting__c custom setting named ‘Default’. This has a field called Implementing_Class__c that I’ve set to ‘ComplexAccountDiscount’:

Account_Discount_Setting__c setting=
    Account_Discount_Setting__c.getInstance('Default');

Type impl = Type.forName(setting.Implementing_Class__c);
AccountDiscountInterface adi=
    (AccountDiscountInterface) impl.newInstance();

System.debug('Discount for Burlington (apparel) = '
             + adi.GetDiscount('00124000004RIGFAA4'));

Now my code has no knowledge of the class that is implementing the discount calculation, it simply creates whatever class has been configured and uses that. If my customer wants to switch the implementation, it’s as simple as changing a field in a custom setting. It also allows me to set up a different version for testing - unit tests should be as simple as possible so if I have a genuinely complex discount implementation I probably don’t want to use that when testing the consuming code in case it has a side effect that my test isn’t expecting - I’d still test that implementation, but in it’s own unit tests.

So these should be used always?

As I mentioned earlier, you don’t always need an interface. They do add a little overhead, as I now have an additional custom setting and interface to create and deploy. If the implementation is never likely to change then there’s no point in abstracting it away like this. We use them a lot in BrightMedia as it allows us to have a selection of implementations for services that customers can chose between.