├── README.md ├── df11 ├── BatchApexSequencing │ └── classes │ │ ├── BatchScheduler.cls │ │ ├── BatchScheduler.cls-meta.xml │ │ ├── FirstBatch.cls │ │ ├── FirstBatch.cls-meta.xml │ │ ├── SecondBatch.cls │ │ └── SecondBatch.cls-meta.xml ├── Context-Overload │ ├── ExecuteAnonymous.txt │ ├── classes │ │ ├── Metaphone.cls │ │ └── Metaphone.cls-meta.xml │ ├── readme.txt │ └── triggers │ │ ├── AccountTrigger.trigger │ │ └── AccountTrigger.trigger-meta.xml ├── Environment-Variables │ ├── ExecuteAnonymous.txt │ ├── classes │ │ ├── AcmeEnvironment.cls │ │ ├── AcmeEnvironment.cls-meta.xml │ │ ├── EnvironmentVariable.cls │ │ ├── EnvironmentVariable.cls-meta.xml │ │ ├── EnvironmentVariableTest.cls │ │ └── EnvironmentVariableTest.cls-meta.xml │ ├── objects │ │ └── EnvironmentVariable__c.object │ └── readme.txt ├── Pagination │ ├── classes │ │ ├── DF2011_PaginationController.cls │ │ └── DF2011_PaginationController.cls-meta.xml │ └── pages │ │ ├── Pagination.page │ │ └── Pagination.page-meta.xml └── README.md └── df12 ├── BulkStateTransition ├── README.md ├── classes │ ├── OrderClass.cls │ └── OrderClass.cls-meta.xml ├── objects │ └── Order__c.object └── triggers │ ├── OpptyTrigger.trigger │ └── OpptyTrigger.trigger-meta.xml ├── Composite ├── AndComposite.cls ├── Composite.cls ├── Expression.cls ├── OrComposite.cls ├── README.md └── Variable.cls ├── README.md ├── Singleton ├── AccountFooRecordType.cls ├── AccountTrigger.trigger └── README.md ├── Strategy ├── GeocodeService.cls ├── Geocoder.cls ├── GlobalVariable__c.object ├── GoogleMapsImpl.cls ├── MapQuestImpl.cls └── README.md └── sObjectDecorator ├── README.md ├── classes ├── DecoratedWeather.cls ├── DecoratedWeather.cls-meta.xml ├── weather_controller.cls └── weather_controller.cls-meta.xml ├── objects └── Weather__c.object └── pages ├── weather.page └── weather.page-meta.xml /README.md: -------------------------------------------------------------------------------- 1 | Contains code examples from the Dreamforce "Apex Design Patterns" session. 2 | 3 | This session has been conducted twice, hence the sub-folders df11 and df12. -------------------------------------------------------------------------------- /df11/BatchApexSequencing/classes/BatchScheduler.cls: -------------------------------------------------------------------------------- 1 | global class BatchScheduler implements Schedulable{ 2 | private Integer batchMode; 3 | public static Integer STEP_1=1; 4 | public static Integer STEP_2=2; 5 | 6 | global BatchScheduler(Integer mode){ 7 | this.batchMode=mode; 8 | } 9 | global void execute(SchedulableContext sc) { 10 | try { 11 | //Abort the existing schedule as this is a 1 time scheduled 12 | //job and should not run again 13 | CronTrigger ct = [SELECT id,CronExpression, 14 | TimesTriggered, NextFireTime 15 | FROM CronTrigger 16 | WHERE id = :sc.getTriggerId()]; 17 | if (ct != null){ 18 | System.abortJob(ct.Id); 19 | } 20 | } catch (Exception e) { 21 | System.debug('There are no jobs currently scheduled.'); 22 | } 23 | //Call your next Apex Batch class 24 | if(batchMode == STEP_2){ 25 | SecondBatch 2ndBatch = new SecondBatch(); 26 | Database.executeBatch(2ndBatch); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /df11/BatchApexSequencing/classes/BatchScheduler.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df11/BatchApexSequencing/classes/FirstBatch.cls: -------------------------------------------------------------------------------- 1 | global class FirstBatch implements Database.Batchable,Database.Stateful{ 2 | private Integer mode; 3 | global FirstBatch(Integer mode){ 4 | this.batchMode=mode; 5 | } 6 | 7 | global Database.QueryLocator start(Database.BatchableContext BC){ 8 | //Execute your query 9 | } 10 | global void execute(Database.BatchableContext BC, List scope){ 11 | //Process each batch of records 12 | } 13 | global void finish(Database.BatchableContext BC){ 14 | 15 | //Do any cleanup process for FirstBatch 16 | 17 | //Send any notifications 18 | 19 | //Schedule the next batch to execute 1 minute after the current one ends. 20 | //1. Create the cron expression 21 | String scheduleString='0';//0 seconds 22 | Datetime currTime = System.now(); 23 | currTime = currTime.addMinutes(1); 24 | scheduleString+=' '+currTime.minute(); 25 | scheduleString+=' '+currTime.hour(); 26 | scheduleString+=' '+currTime.day(); 27 | scheduleString+=' '+currTime.month(); 28 | scheduleString+=' ?'; 29 | scheduleString+=' '+currTime.year(); 30 | 31 | //2. Create a good jobname 32 | //Create a job name that is easy to monitor 33 | String jobName = 'MYBATCH-'+System.now().format('MM-dd-yyyy-hh:'); 34 | 35 | //2. Schedule the job to run 36 | BatchSchedular stsUpdate = new BatchSchedular(BAtchScheduler.STEP_2); 37 | System.schedule(jobName,scheduleString,stsUpdate); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /df11/BatchApexSequencing/classes/FirstBatch.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df11/BatchApexSequencing/classes/SecondBatch.cls: -------------------------------------------------------------------------------- 1 | global class SecondBatch implements Database.Batchable,Database.Stateful{ 2 | 3 | global Database.QueryLocator start(Database.BatchableContext BC){ 4 | //Execute your query 5 | } 6 | global void execute(Database.BatchableContext BC, List scope){ 7 | //Process each batch of records 8 | } 9 | global void finish(Database.BatchableContext BC){ 10 | 11 | //Do any cleanup process for FirstBatch 12 | 13 | //Send any notifications 14 | 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /df11/BatchApexSequencing/classes/SecondBatch.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df11/Context-Overload/ExecuteAnonymous.txt: -------------------------------------------------------------------------------- 1 | System.debug(Metaphone.calculate('Account','Name','NameMetaphoneEncoded__c')); 2 | System.debug(Metaphone.calculate('Contact','FirstName','FirstNameMetaphoneEncoded__c')); 3 | System.debug(Metaphone.calculate('Contact','LastName','LastNameMetaphoneEncoded__c')); -------------------------------------------------------------------------------- /df11/Context-Overload/classes/Metaphone.cls: -------------------------------------------------------------------------------- 1 | global class Metaphone implements Database.Batchable{ 2 | 3 | private String objectName; 4 | private String source; 5 | private String destination; 6 | 7 | private Metaphone(String objectName, String source, String destination){ 8 | this.objectName = objectName; 9 | this.source = source; 10 | this.destination = destination; 11 | } 12 | 13 | global Database.QueryLocator start(Database.BatchableContext context){ 14 | return Database.getQueryLocator('select ' + String.escapeSingleQuotes(source) 15 | + ',' + String.escapeSingleQuotes(destination) 16 | + ' from ' + String.escapeSingleQuotes(objectName)); 17 | } 18 | 19 | global void execute(Database.BatchableContext context, List records){ 20 | calculate(records,source,destination); 21 | update records; 22 | } 23 | 24 | global void finish(Database.BatchableContext context){} 25 | 26 | global static ID calculate(String objectName, String source, String destination){ 27 | return Database.executeBatch(new Metaphone(objectName,source,destination)); 28 | } 29 | 30 | global static void calculate(List records, String source, String destination){ 31 | for(SObject record : records){ 32 | record.put(destination,Metaphone.calculate(String.valueOf(record.get(source)))); 33 | } 34 | } 35 | 36 | global static String calculate(String str){ 37 | if(str == null) str = ''; 38 | //Start with a string that is all uppercase 39 | String startStr = str.toUpperCase(); 40 | 41 | String cs = ''; //this will be our "compare string" 42 | String result = ''; //this is our result which we will return at the end of this method 43 | 44 | //check each letter in the string to see if the next letter is the same, if so, drop it, unless it is C 45 | for(Integer i=0;i VOWELS = new Set(); 65 | VOWELS.add('A'); 66 | VOWELS.add('E'); 67 | VOWELS.add('I'); 68 | VOWELS.add('O'); 69 | VOWELS.add('U'); 70 | 71 | if(cs.endsWith('MB')){ 72 | cs = cs.substring(0,cs.length()-1); 73 | } 74 | 75 | //Create integer to hold length of the compare string. cleans up the code a little. 76 | Integer cslen = cs.length(); 77 | 78 | //loop through the string and check each letter, matching against the metaphone patters. 79 | for(Integer i=0;i0 && cs.substring(i-1,i).equals('S')){ 101 | result += 'K'; 102 | i++; 103 | }else{ 104 | result += 'X'; 105 | i++; 106 | } 107 | }else if(i0 && cs.substring(i-1,i).equals('S')){ 114 | //do nothing and drop C, it is slient 115 | }else{ 116 | result += 'S'; 117 | } 118 | }else{ 119 | result += 'K'; 120 | } 121 | continue; 122 | } 123 | 124 | //'D' transforms to 'J' if followed by 'GE', 'GY', or 'GI'. Otherwise, 'D' transforms to 'T'. 125 | if(cs.substring(i,i+1).equals('D')){ 126 | if(i2 && cs.substring(1,2).equals('H') && VOWELS.contains(cs.substring(2,3))){ 151 | result += 'K'; //GH at the beginning should result in 'K' 152 | i++; //we know H is next and is taken care of, so skip it 153 | }else if((i0 && VOWELS.contains(cs.substring(i-1,i))){ 180 | //we have a vowel before H 181 | if((i0 && cs.substring(i-1,i).equals('C')){ 200 | //do nothing since the K is silent (the C was already transformed to K earlier 201 | }else{ 202 | result +='K'; 203 | } 204 | continue; 205 | } 206 | 207 | if(cs.substring(i,i+1).equals('L')){ 208 | result += 'L'; 209 | continue; 210 | } 211 | 212 | if(cs.substring(i,i+1).equals('M')){ 213 | result += 'M'; 214 | continue; 215 | } 216 | 217 | if(cs.substring(i,i+1).equals('N')){ 218 | result += 'N'; 219 | continue; 220 | } 221 | 222 | //'PH' transforms to 'F'. (the H will get dropped later) 223 | if(cs.substring(i,i+1).equals('P')){ 224 | if(i1 && cs.substring(i+1,i+2).equals('H')){ 284 | result+='W'; 285 | i++; 286 | }else if(i 2 | 3 | 20.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df11/Context-Overload/readme.txt: -------------------------------------------------------------------------------- 1 | "Context Overload" is a Force.com pattern presented by Richard Vanhook at Dreamforce 2011 during the session titled "Apex Design Patterns and Best Practices". 2 | 3 | This patterns defines a way in which to run logic in any context. The code in this example uses the Metaphone algorithm as a concrete piece of logic. The basic idea is to consolidate your logic into a single class and allow the logic to be utilized in any context via method overloading. See Metaphone.cls. 4 | 5 | ===================================================== 6 | Introduction to Metaphone 7 | ===================================================== 8 | A phonetic algorithm for indexing words by their English pronunciation. Is used in spell-checkers and search algorithms. 9 | 10 | Examples: 11 | Metaphone.calculate('Richard') => RXRT 12 | Metaphone.calculate('Richerd') => RXRT 13 | Metaphone.calculate('Richrd') => RXRT 14 | Metaphone.calculate('Richrrd') => RXRT 15 | 16 | Credits to Kyle Thornton & Cloudspokes for Metaphone algorithm implementation. 17 | 18 | ===================================================== 19 | Pattern Benefits & Tips 20 | ===================================================== 21 | Reuse logic in any of the following contexts: 22 | -Execute Anonymous 23 | -Batch Apex 24 | -Synchronous Trigger 25 | -Asynchronous (@future) Trigger 26 | -Visualforce Controller 27 | -Scheduled Apex 28 | -Apex Web Service 29 | Ability to control sharing (with/without) 30 | Makes code more flexible/reusable 31 | -------------------------------------------------------------------------------- /df11/Context-Overload/triggers/AccountTrigger.trigger: -------------------------------------------------------------------------------- 1 | trigger AccountTrigger on Account (before insert, before update) { 2 | Metaphone.calculate(Trigger.new,'Name','NameMetaphoneEncoded__c'); 3 | } -------------------------------------------------------------------------------- /df11/Context-Overload/triggers/AccountTrigger.trigger-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df11/Environment-Variables/ExecuteAnonymous.txt: -------------------------------------------------------------------------------- 1 | AcmeEnvironment.amazonS3PrivateKey = 'abc123'; 2 | System.debug(AcmeEnvironment.amazonS3PrivateKey); -------------------------------------------------------------------------------- /df11/Environment-Variables/classes/AcmeEnvironment.cls: -------------------------------------------------------------------------------- 1 | global class AcmeEnvironment { 2 | 3 | global static final String KEY_AMAZON_S3_PRIVATE_KEY = 'AmazonS3.PrivateKey'; 4 | 5 | global static String amazonS3PrivateKey { 6 | set{ EnvironmentVariable.put(KEY_AMAZON_S3_PRIVATE_KEY,value); } 7 | get{ return EnvironmentVariable.get(KEY_AMAZON_S3_PRIVATE_KEY); } 8 | } 9 | 10 | //normally I put test methods in their own class 11 | //doing this for readability 12 | private static testmethod void test_podName(){ 13 | AcmeEnvironment.amazonS3PrivateKey = 'abc123'; 14 | System.assertEquals('abc123',EnvironmentVariable.get(KEY_AMAZON_S3_PRIVATE_KEY)); 15 | System.assertEquals('abc123',AcmeEnvironment.amazonS3PrivateKey); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /df11/Environment-Variables/classes/AcmeEnvironment.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df11/Environment-Variables/classes/EnvironmentVariable.cls: -------------------------------------------------------------------------------- 1 | global class EnvironmentVariable { 2 | 3 | global static String get(String name){ 4 | String returnValue = null; 5 | final Map all = EnvironmentVariable__c.getAll(); 6 | if( name != null 7 | && name.trim() != null 8 | && name.trim().length() > 0 9 | && all != null 10 | && all.get(name) != null 11 | ){ 12 | returnValue = all.get(name).value__c; 13 | } 14 | return returnValue; 15 | } 16 | 17 | global static String put(String name, String value){ 18 | String returnValue = null; //the previous value 19 | if( name != null 20 | && name.trim() != null 21 | && name.trim().length() != 0 22 | ){ 23 | EnvironmentVariable__c record = null; 24 | try{ 25 | record = [ 26 | select id,value__c 27 | from EnvironmentVariable__c 28 | where name = :name 29 | ]; 30 | }catch(QueryException e){} 31 | if(record == null){ 32 | record = new EnvironmentVariable__c( 33 | name = name 34 | ,value__c = value 35 | ); 36 | } else { 37 | returnValue = record.value__c; 38 | } 39 | record.value__c = value; 40 | if(record.id != null){ 41 | update record; 42 | } else { 43 | insert record; 44 | } 45 | } 46 | return returnValue; 47 | } 48 | 49 | 50 | } -------------------------------------------------------------------------------- /df11/Environment-Variables/classes/EnvironmentVariable.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df11/Environment-Variables/classes/EnvironmentVariableTest.cls: -------------------------------------------------------------------------------- 1 | @IsTest 2 | private class EnvironmentVariableTest { 3 | 4 | private static testmethod void test_get_bad_input(){ 5 | //no asserts, just making sure this doesn't throw an exception 6 | // if an exception were thrown, then this test would fail 7 | System.assertEquals(null,EnvironmentVariable.get(null)); 8 | System.assertEquals(null,EnvironmentVariable.get(' ')); 9 | } 10 | 11 | private static testmethod void test_put_bad_input(){ 12 | //no asserts, just making sure this doesn't throw an exception 13 | // if an exception were thrown, then this test would fail 14 | System.assertEquals(null,EnvironmentVariable.put(null,null)); 15 | System.assertEquals(null,EnvironmentVariable.put(' ',null)); 16 | } 17 | 18 | private static testmethod void test_put_insert(){ 19 | deleteEnvironmentVariableWithNameIfExists('test 123'); 20 | 21 | String previousValue = EnvironmentVariable.put('test 123',null); 22 | assertEnvironmentVariableExistsWith('test 123',null); 23 | System.assertEquals(null,previousValue); 24 | System.assertEquals(null,EnvironmentVariable.get('test 123')); 25 | 26 | previousValue = EnvironmentVariable.put('test 123','xyz'); 27 | assertEnvironmentVariableExistsWith('test 123','xyz'); 28 | System.assertEquals(null,previousValue); 29 | System.assertEquals('xyz',EnvironmentVariable.get('test 123')); 30 | 31 | previousValue = EnvironmentVariable.put('test 123','abc'); 32 | assertEnvironmentVariableExistsWith('test 123','abc'); 33 | System.assertEquals('xyz',previousValue); 34 | System.assertEquals('abc',EnvironmentVariable.get('test 123')); 35 | } 36 | 37 | private static testmethod void test_put_update(){ 38 | deleteEnvironmentVariableWithNameIfExists('test 123'); 39 | EnvironmentVariable__c record = new EnvironmentVariable__c( 40 | name = 'test 123' 41 | ,Value__c = 'xyz' 42 | ); 43 | insert record; 44 | assertEnvironmentVariableExistsWith('test 123','xyz'); 45 | 46 | String previousValue = EnvironmentVariable.put('test 123','abc'); 47 | assertEnvironmentVariableExistsWith('test 123','abc'); 48 | System.assertEquals('xyz',previousValue); 49 | System.assertEquals('abc',EnvironmentVariable.get('test 123')); 50 | } 51 | 52 | private static void deleteEnvironmentVariableWithNameIfExists(String name){ 53 | EnvironmentVariable__c record = null; 54 | try{ 55 | record = [ 56 | select id 57 | from EnvironmentVariable__c 58 | where name = :name 59 | limit 1 60 | ]; 61 | }catch(QueryException e){} 62 | if(record != null) delete record; 63 | } 64 | 65 | private static void assertEnvironmentVariableExistsWith(String name, String value){ 66 | EnvironmentVariable__c record = null; 67 | try{ 68 | record = [ 69 | select value__c 70 | from EnvironmentVariable__c 71 | where name = :name 72 | limit 1 73 | ]; 74 | }catch(QueryException e){} 75 | System.assertNotEquals(null,record); 76 | System.assertEquals (value,record.value__c); 77 | } 78 | 79 | 80 | } -------------------------------------------------------------------------------- /df11/Environment-Variables/classes/EnvironmentVariableTest.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df11/Environment-Variables/objects/EnvironmentVariable__c.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | List 4 | Public 5 | false 6 | false 7 | 8 | Value__c 9 | false 10 | 11 | 128 12 | false 13 | Text 14 | false 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /df11/Environment-Variables/readme.txt: -------------------------------------------------------------------------------- 1 | "Environment Variables" is a Force.com pattern presented by Richard Vanhook at Dreamforce 2011 during the session titled "Apex Design Patterns and Best Practices". 2 | 3 | This patterns defines a way to store persistent environment or configuration variables. See AcmeEnvironment.cls. 4 | -------------------------------------------------------------------------------- /df11/Pagination/classes/DF2011_PaginationController.cls: -------------------------------------------------------------------------------- 1 | public class DF2011_PaginationController{ 2 | 3 | public ApexPages.StandardSetController setController {get; private set;} 4 | public Integer empSize {get; set;} 5 | 6 | public DF2011_PaginationController(){ 7 | queryForRecords(); 8 | } 9 | public PageReference filterRecords(){ 10 | queryForRecords(); 11 | return null; 12 | } 13 | public List getRecords(){ 14 | return setController.getRecords(); 15 | } 16 | private void queryForRecords(){ 17 | String soqlToExecute ='select Id,IsPersonAccount,Name,BillingStreet,BillingState,BillingCountry,BillingPostalCode,NumberOfEmployees from Account'; if(empSize !=null){ 18 | soqlToExecute +=' where NumberOfEmployees >=:empSize'; 19 | } 20 | setController = new ApexPages.StandardSetController(Database.getQueryLocator(soqlToExecute)); 21 | 22 | } 23 | 24 | 25 | 26 | } -------------------------------------------------------------------------------- /df11/Pagination/classes/DF2011_PaginationController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df11/Pagination/pages/Pagination.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Employee Size: 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /df11/Pagination/pages/Pagination.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /df11/README.md: -------------------------------------------------------------------------------- 1 | Code shown during Dreamforce 2011 **Apex Design Patterns and Best Practices** session on August 30, 2011 as presented by [Anand Narasimhan](https://twitter.com/anand_bn) and [Richard Vanhook](https://twitter.com/richardvanhook). 2 | 3 | To dive into a specific pattern, click a link above 4 | 5 | [View Session Recording on Youtube](http://www.youtube.com/watch?v=Sd_n_hbVuBE) 6 | 7 | [View Session info in Dreamforce App](https://dreamevent.my.salesforce.com/a0930000009YIOhAAO?nooverride=1) 8 | 9 | [Download Slides from Dreamforce App](https://dreamevent.my.salesforce.com/sfc/servlet.shepherd/version/download/0683000000157EL?asPdf=false&operationContext=CHATTER) -------------------------------------------------------------------------------- /df12/BulkStateTransition/README.md: -------------------------------------------------------------------------------- 1 | The code for Bulk State Transition is deployable via the Salesforce Migration Toolkit. 2 | 3 | Alternatively: 4 | i) Create an Order sObject with a lookup to Opportunity named Opportunity 5 | ii) Manually create the Apex class and Apex trigger -------------------------------------------------------------------------------- /df12/BulkStateTransition/classes/OrderClass.cls: -------------------------------------------------------------------------------- 1 | public with sharing class OrderClass { 2 | 3 | public void CreateOrdersFromOpptys(List opptyList) { 4 | List orderList = new List(); 5 | for (Opportunity oppty : opptyList) { 6 | Order__c order = new Order__c(); 7 | order.name = 'Order for Opportunity ' + oppty.name; 8 | order.opportunity__c = oppty.id; 9 | orderList.add(order); 10 | } 11 | insert orderList ; 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /df12/BulkStateTransition/classes/OrderClass.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df12/BulkStateTransition/objects/Order__c.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Accept 5 | Default 6 | 7 | 8 | Clone 9 | Default 10 | 11 | 12 | Delete 13 | Default 14 | 15 | 16 | Edit 17 | Default 18 | 19 | 20 | List 21 | Default 22 | 23 | 24 | New 25 | Default 26 | 27 | 28 | Tab 29 | Default 30 | 31 | 32 | View 33 | Default 34 | 35 | Deployed 36 | false 37 | false 38 | false 39 | false 40 | 41 | Opportunity__c 42 | SetNull 43 | false 44 | 45 | Opportunity 46 | Orders 47 | Orders 48 | false 49 | Lookup 50 | 51 | 52 | 53 | All 54 | Everything 55 | 56 | 57 | 58 | 59 | Text 60 | 61 | Orders 62 | 63 | ReadWrite 64 | 65 | -------------------------------------------------------------------------------- /df12/BulkStateTransition/triggers/OpptyTrigger.trigger: -------------------------------------------------------------------------------- 1 | trigger OpptyTrigger on Opportunity (after insert, after update) { 2 | 3 | if (trigger.isAfter && (trigger.isInsert || trigger.isUpdate)) { 4 | List closedOpptyList = new List(); 5 | for (Opportunity oppty : trigger.new) { 6 | if (oppty.isClosed && (trigger.isInsert || 7 | (trigger.isUpdate && !trigger.oldMap.get(oppty.id).isClosed))) 8 | closedOpptyList.add(oppty); 9 | } 10 | if (!closedOpptyList.isEmpty()) 11 | new OrderClass().CreateOrdersFromOpptys(closedOpptyList); 12 | } 13 | } -------------------------------------------------------------------------------- /df12/BulkStateTransition/triggers/OpptyTrigger.trigger-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df12/Composite/AndComposite.cls: -------------------------------------------------------------------------------- 1 | public class AndComposite extends Composite{ 2 | public override Boolean evaluate(){ 3 | for(Expression expr : children) if(!expr.evaluate()) return false; 4 | return true; 5 | } 6 | } -------------------------------------------------------------------------------- /df12/Composite/Composite.cls: -------------------------------------------------------------------------------- 1 | public abstract class Composite implements Expression{ 2 | public List children {get; private set;} 3 | public Composite(){ 4 | this.children = new List(); 5 | } 6 | public Expression add(Expression expr){ 7 | children.add(expr); 8 | return this; 9 | } 10 | public Expression set(String name, Boolean value){ 11 | for(Expression expr : children) expr.set(name,value); 12 | return this; 13 | } 14 | public abstract Boolean evaluate(); 15 | public Boolean hasChildren{get{ 16 | return !children.isEmpty(); 17 | }} 18 | } -------------------------------------------------------------------------------- /df12/Composite/Expression.cls: -------------------------------------------------------------------------------- 1 | public interface Expression { 2 | Expression add(Expression expr); 3 | Expression set(String name, Boolean value); 4 | Boolean evaluate(); 5 | } -------------------------------------------------------------------------------- /df12/Composite/OrComposite.cls: -------------------------------------------------------------------------------- 1 | public class OrComposite extends Composite{ 2 | public override Boolean evaluate(){ 3 | for(Expression expr : children)if(expr.evaluate()) return true; 4 | return false; 5 | } 6 | } -------------------------------------------------------------------------------- /df12/Composite/README.md: -------------------------------------------------------------------------------- 1 | Example implementation of Composite Design Pattern in Apex demonstrating representation of below expressions. 2 | 3 | 1 AND 2 4 | 1 OR (2 AND 3) 5 | (1 AND 2) OR ((3 OR 4) AND 5) 6 | 7 | ## Usage 8 | 9 | //1 OR (2 AND 3) 10 | Expression expr = (new OrComposite()) 11 | .add(new Variable('1')) 12 | .add((new AndComposite()) 13 | .add(new Variable('2')) 14 | .add(new Variable('3')) 15 | ) 16 | .set('1',false) 17 | .set('2',true) 18 | .set('3',false); 19 | 20 | System.debug(expr.evaluate()); 21 | //FALSE OR (TRUE AND FALSE) => FALSE 22 | 23 | expr.set('3',true); 24 | 25 | System.debug(expr.evaluate()); 26 | //FALSE OR (TRUE AND TRUE) => TRUE -------------------------------------------------------------------------------- /df12/Composite/Variable.cls: -------------------------------------------------------------------------------- 1 | public with sharing class Variable implements Expression{ 2 | public String name {get;private set;} 3 | public Boolean value {get;private set;} 4 | public Variable(String name){ this.name = name; } 5 | public Expression add(Expression expr){ return this; } 6 | public Expression set(String name, Boolean value){ 7 | if(this.name != null && this.name.equalsIgnoreCase(name)){ 8 | this.value = value; 9 | } 10 | return this; 11 | } 12 | public Boolean evaluate(){ 13 | return value; 14 | } 15 | } -------------------------------------------------------------------------------- /df12/README.md: -------------------------------------------------------------------------------- 1 | Code shown during Dreamforce 2012 **Apex Design Patterns** session on September 18, 2012 as presented by [Dennis Thong](https://twitter.com/denniscfthong) and [Richard Vanhook](https://twitter.com/richardvanhook). 2 | 3 | To dive into a specific pattern, click a link above 4 | 5 | View Session Recording on Youtube <- will update as soon as it's available 6 | 7 | [View Session info in Dreamforce App](https://dreamevent.my.salesforce.com/apex/ActivityList?type=Dreamforce#a093000000VhYLkAAN) 8 | 9 | [Download Slides from Dreamforce App](https://dreamevent.my.salesforce.com/sfc/servlet.shepherd/version/download/06830000002rB4SAAU?operationContext=CHATTER) 10 | -------------------------------------------------------------------------------- /df12/Singleton/AccountFooRecordType.cls: -------------------------------------------------------------------------------- 1 | public class AccountFooRecordType { 2 | private static AccountFooRecordType instance = null; 3 | public String id {get;private set;} 4 | private AccountFooRecordType(){ 5 | id = Account.sObjectType.getDescribe() 6 | .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 7 | } 8 | public static AccountFooRecordType getInstance(){ 9 | if(instance == null) instance = new AccountFooRecordType(); 10 | return instance; 11 | } 12 | } -------------------------------------------------------------------------------- /df12/Singleton/AccountTrigger.trigger: -------------------------------------------------------------------------------- 1 | trigger AccountTrigger on Account (before insert, before update) { 2 | for(Account record : Trigger.new){ 3 | AccountFooRecordType rt = AccountFooRecordType.getInstance(); 4 | } 5 | } -------------------------------------------------------------------------------- /df12/Singleton/README.md: -------------------------------------------------------------------------------- 1 | Example implementation of Singleton assuming below is starting code. 2 | 3 | trigger AccountTrigger on Account (before insert, before update) { 4 | for(Account record : Trigger.new){ 5 | AccountFooRecordType rt = new AccountFooRecordType(); 6 | } 7 | } 8 | 9 | 10 | public class AccountFooRecordType { 11 | public String id {get;private set;} 12 | public AccountFooRecordType(){ 13 | id = Account.sObjectType.getDescribe() 14 | .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /df12/Strategy/GeocodeService.cls: -------------------------------------------------------------------------------- 1 | public interface GeocodeService{ 2 | List getLatLong(String address); 3 | } -------------------------------------------------------------------------------- /df12/Strategy/Geocoder.cls: -------------------------------------------------------------------------------- 1 | public class Geocoder { 2 | public class NameException extends Exception{} 3 | public static final Map strategies; 4 | static{ 5 | GlobalVariable__c gv = GlobalVariable__c.getInstance('strategies'); 6 | List strategyNames = new List(); 7 | if(gv != null && gv.value__c != null) strategyNames = gv.value__c.split(','); 8 | strategies = new Map(); 9 | for(String name : strategyNames){ 10 | try{ 11 | strategies.put(name 12 | ,(GeocodeService)Type.forName(name+'impl').newInstance()); 13 | }catch(Exception e){continue;} //skip bad config silently 14 | } 15 | } 16 | private GeocodeService strategy; 17 | public Geocoder(String s){ 18 | if(!strategies.containsKey(s)) throw new NameException(s + ' not found'); 19 | strategy = strategies.get(s); 20 | } 21 | public List getLatLong(String address){ 22 | return strategy.getLatLong(address); 23 | } 24 | } -------------------------------------------------------------------------------- /df12/Strategy/GlobalVariable__c.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | List 4 | Public 5 | false 6 | 7 | Value__c 8 | false 9 | 10 | 255 11 | false 12 | Text 13 | false 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /df12/Strategy/GoogleMapsImpl.cls: -------------------------------------------------------------------------------- 1 | public class GoogleMapsImpl implements GeocodeService{ 2 | public List getLatLong(String address){ 3 | return new List{0,0}; 4 | } 5 | } -------------------------------------------------------------------------------- /df12/Strategy/MapQuestImpl.cls: -------------------------------------------------------------------------------- 1 | public class MapQuestImpl implements GeocodeService{ 2 | public List getLatLong(String address){ 3 | return new List{1,1}; 4 | } 5 | } -------------------------------------------------------------------------------- /df12/Strategy/README.md: -------------------------------------------------------------------------------- 1 | Example implementation of Strategy Design Pattern in Apex using Geocoding as the problem domain. 2 | 3 | ## Setup 4 |
    5 |
  1. Deploy Custom Setting: GlobalVariable__c
  2. 6 |
  3. Deploy four classes: Geocoder, GeocodeService, GoogleMapsImpl, and MapQuestImpl
  4. 7 |
  5. Run following code to create record in Custom Setting: 8 |
  6. 9 |
10 | GlobalVariable__c record = null; 11 | try{ 12 | record = [select id,value__c from GlobalVariable__c where name = 'strategies']; 13 | }catch(QueryException e){} 14 | System.debug(record); 15 | if(record == null) record = new GlobalVariable__c(name= 'strategies'); 16 | record.value__c = 'googlemaps,mapquest'; 17 | if(record.id != null) update record; 18 | else insert record; 19 | 20 | ## Usage 21 | System.debug((new Geocoder('googlemaps')).getLatLong('moscone center')); 22 | //=>DEBUG|(0.0, 0.0) 23 | System.debug((new Geocoder('mapquest')).getLatLong('moscone center')); 24 | //=>DEBUG|(1.0, 1.0) 25 | -------------------------------------------------------------------------------- /df12/sObjectDecorator/README.md: -------------------------------------------------------------------------------- 1 | The code for sObject Decorator is deployable via the Salesforce Migration Toolkit. 2 | 3 | Alternatively: 4 | i) Create an Weather sObject with the following fields: 5 | - Temperature - Decimal(5,2) 6 | 7 | ii) Manually create the following classes: 8 | - DecoratedWeather 9 | - weather_controller 10 | 11 | iii) Manually create the VF page -------------------------------------------------------------------------------- /df12/sObjectDecorator/classes/DecoratedWeather.cls: -------------------------------------------------------------------------------- 1 | public with sharing class DecoratedWeather { 2 | 3 | /* class that wraps the weather custom object */ 4 | 5 | public Weather__c weather { get; set; } 6 | public Decimal tempInCelcius { 7 | get { 8 | if (weather != null && tempInCelcius == null ) 9 | tempInCelcius = (5.0/9.0 * (weather.temperature__c - 32)).setScale(2); 10 | 11 | return tempInCelcius; 12 | } 13 | set { 14 | if (weather != null && value != null ) { 15 | weather.temperature__c = ((9.0/5.0) * (value)).setScale(2) + 32; 16 | } 17 | tempInCelcius = value; 18 | } 19 | } 20 | 21 | public DecoratedWeather (Weather__c weather) { 22 | this.weather = weather; 23 | } 24 | 25 | 26 | } -------------------------------------------------------------------------------- /df12/sObjectDecorator/classes/DecoratedWeather.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df12/sObjectDecorator/classes/weather_controller.cls: -------------------------------------------------------------------------------- 1 | public class weather_controller { 2 | 3 | /* Custom controller */ 4 | 5 | /* Property to display list of weather records */ 6 | public List listOfWeather { 7 | get { 8 | if (listOfWeather == null) { 9 | listOfWeather = new List(); 10 | 11 | for (Weather__c weather : [select name, temperature__c from Weather__c]) { 12 | listOfWeather.add(new DecoratedWeather(weather)); 13 | } 14 | 15 | } 16 | 17 | return listOfWeather; 18 | } 19 | 20 | private set; 21 | } 22 | 23 | /* Saves */ 24 | public void SaveRecords() { 25 | List weatherRecords = new List (); 26 | 27 | for (DecoratedWeather d : listOfWeather) { 28 | weatherRecords.add(d.weather); 29 | } 30 | 31 | update weatherRecords; 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /df12/sObjectDecorator/classes/weather_controller.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /df12/sObjectDecorator/objects/Weather__c.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Accept 5 | Default 6 | 7 | 8 | Clone 9 | Default 10 | 11 | 12 | Delete 13 | Default 14 | 15 | 16 | Edit 17 | Default 18 | 19 | 20 | List 21 | Default 22 | 23 | 24 | New 25 | Default 26 | 27 | 28 | Tab 29 | Default 30 | 31 | 32 | View 33 | Default 34 | 35 | Deployed 36 | false 37 | false 38 | false 39 | false 40 | 41 | Temperature__c 42 | false 43 | 44 | 5 45 | true 46 | 2 47 | Number 48 | false 49 | 50 | 51 | 52 | All 53 | Everything 54 | 55 | 56 | 57 | 58 | Text 59 | 60 | Weather 61 | 62 | ReadWrite 63 | 64 | -------------------------------------------------------------------------------- /df12/sObjectDecorator/pages/weather.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /df12/sObjectDecorator/pages/weather.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25.0 4 | 5 | 6 | --------------------------------------------------------------------------------