An Apex Interface can be used to describe a point in your application logic where custom code written by Developer X can be called. For example, in order to provide an alternative means to calculate championship points driven by Developer X, we might expose a global interface describing an application callout that looks like this:
global class ContestantService {global interface IAwardChampionshipPoints { void calculate(List<Contestant__c> contestants); } }
By querying Custom Metadata records from the Callouts custom metadata type, which has been included in the source code for this chapter, code in the application can determine whether Developer X has provided an implementation of this interface to call instead of the standard calculation code.
Using Custom Metadata is an excellent use case for this sort of requirement, since you can declare the callouts your package supports by packaging records. Then, by making certain fields subscriber editable you can allow the subscriber (Developer X) to configure those callouts. This approach in contrast with Custom Settings, avoids additional configuration and risk of misconfiguration.
This screenshot shows how the Callouts Custom Metadata Type is defined. Notice how the Apex Class and Apex Class Namespace Prefix fields are subscriber editable:
The following record is defined by you the packager developer to effectively declare that Award Championship Points callout exists and can be configured. This should also be included in your package:
The following code is a Selector method (see CalloutsSelector
) class that reads the registered interface implementations. It uses the Type.forName
method to construct applicable Apex types and returns a Map
of Apex classes implementing each interface:
public Map<Type, Type> selectAllCallouts() { // Query custom metadata records for callouts Map<Type, Type> calloutsByInterfaceType = new Map<Type, Type>(); for(Callout__mdt record :Database.query(newQueryFactory().toSOQL())) { if(Callout__mdt.ApexCLass__c!=null) { // Namespace of the interface is that // of the custom metadata type Type interfaceType = Type.forName(record.NamespacePrefix, record.InterfaceType__c); // Implementing class can optionally specify // the namespace if needed Type implType = Type.forName(record.ApexCLassNamespacePrefix__c,record.ApexCLass__c); calloutsByInterfaceType.put(interfaceType, implType); } } return calloutsByInterfaceType; }
The following code uses a new Application.Callout
class factory, which is not shown here but is included in the source code of this chapter. This is a simple class factory that uses the above Selector method and exposes the newInstance
method to provide an easy way to instantiate the classes Developer X has registered. The following method in the Contestants
Domain class now looks like this:
public void awardChampionshipPoints(fflib_ISObjectUnitOfWork uow) { // Custom implementation configured by Developer X? Object registeredInterfaceImpl =Application.Callouts.newInstance(ContestantService.IAwardChampionshipPoints.class); if(registeredInterfaceImpl instanceofContestantService.IAwardChampionshipPoints) { // Cast the interface to call the calculate method ContestantService.IAwardChampionshipPointsawardChampionshipPoints =(ContestantService.IAwardChampionshipPoints)registeredInterfaceImpl; // Invoke the custom method from Developer X awardChampionshipPoints.calculate(Records); // Mark dirty on behalf of Developer X for(Contestant__c contestant : (List<Contestant__c>) Records) { uow.registerDirty(contestant); } return; } // Continue with standard implementation...
In the subscriber org or the test development org, Developer X can then implement this interface as follows and configure the Apex Class field on the Callouts record corresponding to the Award Championship Points calllout. The next time the award championship points service is called, this custom logic will be called instead:
The following is a simple Application Callout implementation that assigns points according to the race position rather than using the default calculation:
public class SimpleCalc implements fforce.ContestantService.IAwardChampionshipPoints { public void calculate(List<fforce__Contestant__c> contestants) { // Very simple, points equals race position for(fforce__Contestant__c contestant : contestants) contestant.fforce__ChampionshipPoints__c =contestant.fforce__RacePosition__c; } }
Some aspects to consider when exposing global Apex interfaces are as follows:
IAwardChampionshipPointsExt extends IAwardChampionshipPoints
.3.16.218.221