Find your content:

Search form

You are here

ANY documentation on writing reusable code?

 
Share

I just spent several hours with google etc. trying to find some documentation and I have come up empty handed.

My code right now has a fair percentage of duplication and I would like to fix that by implementing some shared classes. In my limited experience things always go better in SF if you read everything you can find on a topic BEFORE you start hacking away at code.

Can anyone point me to any documentation specifically about writing code using shared classes/libraries?

Thanks!

11/15/2012 11am MDT - Adding more information to the question. In particular about my code that right now is embedded in the controller of a VF page.

Right now I have code that executes when you click a button called 'Loan' on a VF page that...

  • Validates the key data (1 date field, 1 picklist and 1 'other' object) Validations include some queries to check the date against other existing records if these validations fail then the process would stop (pass back a message to be displayed so the user can try again without any updates)
  • Update some fields on my custom object (Equipment__c)
  • Creates a list of child objects and potentially update those
  • Creates a new child object (a history record)
  • Potentially creates a chatter record

What I would like to do is pull that code out of the VF page controller and make a method that perhaps (not sure) would accept the Id of the Equipment__c object along with the date field, picklist value and related object Id and perform all the above and then return either a validation message or some indicator there were no errors and the process completed successfully.

In regards to the 'related object' it will either be a User, Account, Contact or Opportunity and I will know which one it is based on the picklist value.

Then I can call that code in EITHER a VF page, another class, a trigger or really anywhere I need to do the same process.

I really think you could think of this as a method on the Equipment_c object itself... Equipment_c.LoanMe method is the name that comes to mind.

Thanks again.


Attribution to: AngiB

Possible Suggestion/Solution #1

Can you explain with what kind of code duplication you're dealing with? I suppose applying enterprise app design patterns can help out, take a look here: https://github.com/financialforcedev/df12-apex-enterprise-patterns


Attribution to: krobbens

Possible Suggestion/Solution #2

Updated Answer:

What you have described in your updated question is indeed behaviour or process around your Equipement object, a good observation about where to start locating your code btw! So while your requirement does relate to your Equipment object, it is not scoped solely around it (it effects other objects and performs cross object validation). So feels more like a Service level/layer aspect of your application implementation. Which sits above your Domain (object behaviour, aka trigger invoked) level code.

As such I would personally create a EquipmentService class with your LoanMe method on it and reuse that amongst your callers. An optional aspect to defining your service layer, is a further consideration to the fact that everything in the world of Apex / Force.com needs to be bulkified (not just your trigger code).

As such you may consider making such methods take list of parameter sets rather than singular sets of parameters. Given this, I would probably go with ProcessLoans (plural) as a method name. This particular method parameter approach ensures, as per the places you want to reuse this from in your question. That the calling code can make a single 'bulk' call more optimally from either VF or Trigger contexts. Taking this consideration also helps those implementing the service code continue to think in a more bulkified way.

public class EquipmentService
{
    public static void ProcessLoans(List<LoanInfo> loanInfos)
    {
        // Implement logic and throw an exception (see Apex dev guide) for errors
        // e.g. throw new EquipmentServiceException('My error') or
        // return information on success if needed.
        // By accepting a list of information the implementation of this method is 
        // honour bound to ensure it observes bulkification as much as possible.
    }

    // Simple data class to help with passing parameter sets in bulk to methods
    // (depending on complexity of parameters this is not always needed)
    public class LoanInfo
    {
         public Id equipmentId;
         public Date dateEntered;
         public String pickList;
         public Id relatedId;
    }

    public class EquipmentServiceException extends Exception
    {
         // You can put your own exception specific information in here
    }
}

Error Handling: You'll notice I have not considered error handling requirements as part of the return information. For me this is the job of the caller to handle or 'catch' this. That is assuming the Service layer either returns what the caller needs on success or throws validation issues via the Apex language Exception semantics (see Apex docs).

public with sharing class EquipmentController
{
    public Date dateEntered {get;set;}
    public Id relatedId {get;set;} 
    public String picklistValue {get;set;}

    private ApexPages.StandardController controller;        

    public EquipmentController(ApexPages.StandardController controller)
    {
         this.controller = controller;
    }

    public PageReference processLoan()
    {
         try
         {
             // Make a service layer call (single instance in this case)
             EquipmentService.LoanInfo loanInfo = new EquipmentService.LoanInfo();
             loanInfo.equipmentId = controller.getRecord().Id;
             loanInfo.dateEntered = dateEntered;
             loanInfo.pickList = pickListValue;
             loanInfo.relatedId = relatedId;
             EquipmentService.ProcessLoans(new List<EquipmentService.LoanInfo> { loanInfo} );

             // Redirect to another page or display a success message
             // ...
         }
         catch (Exception e)
         {
             // Ensure you have apex:pageMessages component on your page
             ApexPages.addMessages(e);
         }             
         return null;
    }
}

Other Callers. The same calling approach can be used from a Trigger by passing more items in the list, populated from Trigger.new / Trigger.old. You can also easily make this a public facing API by changing the 'public' keyword in your service class with 'global'. This is where the benefit of taking this layered / service oriented approach starts to pay for itself, if you trust your code to call this, why not others? And if so, hey presto you have an API for your application as well!

First Revision of Answer:

"some examples of code duplication... I know I have 3 places where if a custom setting record cannot be found I create it (code in all three places is identical)." AngiB

So instead of doing this in your three places...

MyCustomSetting__c myCustomSetting = MyCustomSetting__c.getInstance();
if (myCustomSetting == null)
{
    // Create custom setting
}

You can do this..

MyCustomSetting__c myCustomSetting = MyCustomSetting.getInstance();

Having created a class mirrored in name by the custom setting for easy identification and to encourage encapsulation of domain logic for this object.

public class MyCustomSetting
{
    public static MyCustomSetting__c getInstance()
    {
         MyCustomSetting__c myCustomSetting = MyCustomSetting__c.getInstance();
         if(myCustomSetting == null)
         {
             // Create custom setting
             myCustomSetting = new MyCustomSetting__c();
             // set fields and scope
             insert myCustomSetting;
         }
         return myCustomSetting;
    }
}

"As well right now I have some code that is embedded in an action in a VF page controller that I am about to write another VF page that will need that code - so I am thinking a separate class that both VF page controllers use is in order rather than a massive copy/paste." AngiB

There are a few options for this, take a look at 'extension' controllers in the docs. However sometimes good old OO programming can suffice, such as inheritance.

public ControllerA
{
    public PageReference someMethod()
    {
    }
}

public ControllerB
{
    public PageReference someMethod()
    {
    }
}

Do this ...

public ControllerBase
{
    public PageReference someMethod()
    {
    }
}

public ControllerA extends ControllerBase
{
}

public ControllerB extends ControllerBase
{
}

Attribution to: Andrew Fawcett

Possible Suggestion/Solution #3

This is a pretty general programming / OO question, so IMO you'll be best off looking at resources that address general design patterns and good OO principles. The most practical book I've ever read that made me a better programmer was Effective Java by Joshua Bloch - even if you don't know Java, it's so close to Apex that it will definitely help you. Design Patterns by Gamma is often recommended, but I didn't find it practical enough back when I was a beginning dev.

For Apex-specific reuse advice there are a couple patterns I use all the time that you may find helpful:

  • no code in triggers: the only code in my triggers are one-line calls to utility or service classes that contain all the logic and often have signatures like checkOpportunitySomething(List<Opportunity> triggerOld, List<Opportunity> triggerNew); I also name 100% of my triggers descriptively, i.e. OpportunityAfterUpdate, and that has all the calls that occur after update.
  • OO encapsulation of object logic: if you have a custom object called My_Object__c, you create a class called MyObject that has constructors that know how to query by certain things, i.e. MyObject(Id objId) loads by ID. Any reusable data manipulation that occurs on set or get of properties, or any other object-specific logic, goes in that class.
  • use Apex Lang: it has tons of useful methods that you should reuse instead of rolling your own. A number of these were recently rolled into core Apex as methods on String. DatabaseUtils in particular is very useful.

Attribution to: jkraybill
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/4423

My Block Status

My Block Content