For example, a partner may have a customer that requires completely different business logic for a certain piece of the partner code. Adding customized code to an otherwise generic package is not the best idea, but is possible. But what if the custom code need to query objects that only exist on the target org?
In this post, we’ll review how to use an extension architecture that uses an interface to generically call other packages or even methods in the target org that are unknown during package build.
First, let’s review a few benefits to using this approach:
- The base package can execute logic related to custom objects that do not exist in every org.
- The base package can dynamically call classes residing in the hosting org or in a separate “extension” package.
- Each implementation of the interface is independent of the others, can have different business logic, and is not affected by other implementations.
Step 1: Create Interface in the Base Package
In this step, we’ll create an interface class and a method that is so generic, that the base class can use it with any number of parameters and can get a response that includes any number of variables.
- Create a new global class inside the main package.
- Create a new global interface inside the new global class.
- Create new generic method inside the new global interface (allows different implementation to use in completely different way). Define the return type as a generic list of object and one parameter as list of object.
Step 2: Create External Class
In this step, we’ll create the external class that implements the base package interface. Parameters that are sent into this class go by the normal apex rules, so maps and lists that are passed can be manipulated in the external class (passed by reference). Since the interface blueprint is flexible, the implementation can be static in case a different API need to be accessed (needs to be static in some cases because a trigger starts the execution).
- Create a new class in a separate package or in the target org itself. Class should implement the interface from Step 1.
- Define a the generic class that is present in the interface. In the class, replace the input param list with variables that have concrete types, and using the new variables, call a class that does all the work. End the method by returning something.
Step 3: Call External Class From Within The Base Package
Now that the internal interface and the external implementation are ready, this new architecture can be used. If the base package may include many implementations of the interface, Use an unprotected custom setting to store and retrieve the name of the external class and the namespace (if in an extension package).
- Check if an external class needs to be executed by checking that it exists in the custom setting.
- Get the class name that needs to be executed. If the class is in a separate package, the package namespace is needs too.
- Use forName method to create a type variable from the string names of the class and namespace.
- Use the type to create a new instance of the class. Cast into the interface since it is a concrete type that is available within the base package.
- Use the class instance to call the generic class in #3.
- Cast the returned object to the appropriate type.