├── 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 | - Deploy Custom Setting: GlobalVariable__c
6 | - Deploy four classes: Geocoder, GeocodeService, GoogleMapsImpl, and MapQuestImpl
7 | - Run following code to create record in Custom Setting:
8 |
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 |
--------------------------------------------------------------------------------