Just as a Class
has variables, interfaces
have methods. A class
can implement an interface if the class
has all of the interface's methods. So, how is this useful? Here's an example:
Given an interface,
public interface anAwesomeInterface{
void customSave();
}
and classes class1
, class2
, and class3
implementing anAwesomeInterface
.
public class class1 implements anAwesomeInterface{
public void customSave(){
// however you save an instance of class1
}
}
public class class2 implements anAwesomeInterface{
public void customSave(){
// however you save an instance of class2
}
}
public class class3 implements anAwesomeInterface{
public void customSave(){
// however you save an instance of class3
}
}
Now let's make some collections. We can do the following:
class1 anExampleOf_class1 = new class1();
class2 anExampleOf_class2 = new class2();
class2 anotherExampleOf_class2 = new class2();
class3 anExampleOf_class3 = new class3();
anAwesomeInterface aai_exampleOfClass1 = new class1();
anAwesomeInterface aai_anotherExmapleOf_class2 = anotherExampleOf_class2;
list<anAwesomeInterface> anAwesomeList = new list<anAwesomeInterface>();
anAwesomeList.add(anExampleOf_class1);
anAwesomeList.add(anExampleOf_class2);
anAwesomeList.add(anotherExampleOf_class2);
anAwesomeList.add(anExampleOf_class3);
anAwesomeList.add(aai_exampleOfClass1);
anAwesomeList.add(aai_anotherExmapleOf_class2);
for(anAwesomeInterface a:anAwesomeList)
a.customSave(); // each gets saved depending on its class's customSave method
Another example would be how Account
implements sObject
. An Account instance can both be an Account and an sObject depending how you want to use it.
Interfaces define common action among classes -- or maybe it's better the other way around, classes that have common actions define an interface.
So, the idea of interfaces is knowing what actions you want to do without specifically defining the class that's using the action.
This comes in handy when you want every class to have a specific action, though each class does that action in its own way.
A good example of this is implementing a Sharing Model for a Salesforce Org. Typically, every object's Oraginization-Wide Default will be set to Private
, and we'll need a trigger on every object to manage it's sharing. Due to the nature of the problem, we'll have to perform an different action for each DML type -- insert, update, and delete -- for each Object! So, to streamline this, create an interface and a static method in a class called Sharing
.
public class Sharing{
public interface sharingInterface{
void shareOnInsert(list<sObject> newList);
void shareOnUpdate(list<sObject> newList,map<id,sObject> oldMap);
void shareOnDelete(map<id,sObject> oldMap);
}
public static void sharingTrigger(string sObjectName,list<sObject> newList, map<id,sObject> oldMap,boolean isInsert,boolean isUpdate,boolean isDelete){
sObjectType objectType=Schema.getGlobalDescribe().get(sObjectName);
if(objectType==null){
// invalid sObjectName
system.debug(system.LoggingLevel.ERROR,'.... ERROR in Sharing.sharingTrigger('+sObjectName+',*,*,'+isInsert+','+isUpdate+','+isDelete+'). INVALID_SOBJECT_NAME: '+sObjectName);
system.assertNotEquals(null,objectType,'.... ERROR in Sharing.sharingTrigger('+sObjectName+',*,*,'+isInsert+','+isUpdate+','+isDelete+'). INVALID_SOBJECT_NAME: '+sObjectName);
}
sharingInterface sharingInterfaceHelper;
try{
sharingInterfaceHelper=(sharingInterface )type.forName(sObjectName.replace('__c','_sharingHelper'));
} catch(exception e){
system.debug(system.LoggingLevel.ERROR,'.... ERROR in Sharing.sharingTrigger('+sObjectName+',*,*,'+isInsert+','+isUpdate+','+isDelete+'). ERROR: '+e);
return;
}
if(isInsert)
sharingInterfaceHelper.shareOnInsert(newList);
if(isUpdate)
sharingInterfaceHelper.shareOnUpdate(newList,oldMap);
if(isDelete)
sharingInterfaceHelper.shareOnDelete(oldMap);
}//END sharingTrigger(list<sObject> newList,map<id,sObject> oldMap,trigger.isInsert,trigger.isUpdate,trigger.isDelete)
}
So, to use this interface, we need to do two things: (1) implement the interface in an actual Class, and (2) use the method in the appropriate trigger. Note that the method sharingTrigger
requires the naming convention of our classes be the same name as the sObject but with the __c
replaced with _sharingHelper
.
What's cool about the method is that we take the string input sObjectName
and use it to create an instance of the class we want that implements sharingInterface
via type.forName()
. Then, since it's an instance of sharingInterface
, we can only use shairngInterface
methods, namely those defined in the interface. (The interface is those methods -- it's what's the same among all classes that implement that interface)
(1) to share the sObject Employee__c
in Sharing
public class Employee_sharingHelper{
public void sharOnInsert(list<sObject> newList){
list<Employee__c> employeeNewList = (list<Employee__c>)newList; // setting trigger context variable to a more useable list
// do whatcha gotta to share Employee__c's on Insert
}
public void sharOnUpdate(list<sObject> newList,map<id,sObject> oldMap){
list<Employee__c> employeeNewList = (list<Employee__c>)newList; // setting trigger context variable to a more useable list
map<id,Employee__c> employeeOldMap=(map<id,Employee__c>)oldMap;
// do whatcha gotta to share Employee__c's on Update. Maybe compare lookup fields, etc.
}
public void sharOnDelete(map<id,sObject> oldMap){
map<id,Employee__c> employeeOldMap=(map<id,Employee__c>)oldMap;
// do whatcha gotta to share Employee__c's on Delete
}
}
Then, to use this, we simply need to add the method sharingTrigger on an Employee__c trigger.
public triger on Employee__c(after insert,after update,after delete){
Sharing.sharingTrigger('Employee__c',trigger.new,trigger.oldMap,trigger.isInsert,trigger.isUpdate,trigger.isDelete);
Why was all of this worth it? Well, (1) every single one of our triggers became one-liners. (2) All logic is stream-lined, so hopefully it's easier to understand and easier to fully-test, and (3) if we had to manage sharing for another object, all we'd have to do is create another class that implements sharingInterface
and then write the one-line trigger. And note that to implement sharingInterface, you have to have the methods shareOnInsert
, shareOnUpdate
, shareOnDelete
, so the developer is forced to write those methods / worry about those scenarios. It's like a Validation Rule to enforce good/smart coding.