├── .eslintignore ├── .forceignore ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .husky └── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── UML ├── Builder │ └── Builder.puml ├── Observer │ └── Observer.puml ├── StatePattern │ └── StatePattern.puml ├── factory method │ ├── DependentPizzaStore.puml │ ├── factorymethod.png │ └── factorymethod.puml ├── interpreter.puml ├── simple factory │ ├── Simple Factory.png │ └── Simple Factory.puml └── stategy │ └── strategy.puml ├── config └── project-scratch-def.json ├── force-app └── main │ └── default │ ├── appMenus │ └── AppSwitcher.appMenu-meta.xml │ ├── applications │ └── Design_Patterns.app-meta.xml │ ├── aura │ └── .eslintrc.json │ ├── classes │ ├── ApexSandbox-io │ │ ├── IsSorted_Test.cls │ │ ├── IsSorted_Test.cls-meta.xml │ │ ├── SecondLargest.cls │ │ ├── SecondLargest.cls-meta.xml │ │ ├── SecondLargest_Tests.cls │ │ ├── SecondLargest_Tests.cls-meta.xml │ │ ├── SortedList.cls │ │ └── SortedList.cls-meta.xml │ ├── Bridge │ │ ├── BlogFramework.cls │ │ ├── BlogFramework.cls-meta.xml │ │ ├── CMSFramework.cls │ │ ├── CMSFramework.cls-meta.xml │ │ ├── ITheme.cls │ │ ├── ITheme.cls-meta.xml │ │ ├── Theme_WhiteBlue.cls │ │ ├── Theme_WhiteBlue.cls-meta.xml │ │ ├── Theme_WhiteGreen.cls │ │ ├── Theme_WhiteGreen.cls-meta.xml │ │ ├── WebFramework.cls │ │ └── WebFramework.cls-meta.xml │ ├── Builder │ │ ├── IUserBuilder.cls │ │ ├── IUserBuilder.cls-meta.xml │ │ ├── NutritionFacts_Builder.cls │ │ ├── NutritionFacts_Builder.cls-meta.xml │ │ ├── NutritionFacts_Fluent.cls │ │ ├── NutritionFacts_Fluent.cls-meta.xml │ │ ├── NutritionalFacts.cls │ │ ├── NutritionalFacts.cls-meta.xml │ │ ├── UserBuilder.cls │ │ ├── UserBuilder.cls-meta.xml │ │ ├── UserBuilder_Tests.cls │ │ ├── UserBuilder_Tests.cls-meta.xml │ │ ├── UserDirector.cls │ │ └── UserDirector.cls-meta.xml │ ├── DML.cls │ ├── DML.cls-meta.xml │ ├── DMLMock.cls │ ├── DMLMock.cls-meta.xml │ ├── Decorator │ │ ├── Beverage.cls │ │ ├── Beverage.cls-meta.xml │ │ ├── CondimentDecorator.cls │ │ ├── CondimentDecorator.cls-meta.xml │ │ ├── Expresso.cls │ │ ├── Expresso.cls-meta.xml │ │ ├── HouseBlend.cls │ │ ├── HouseBlend.cls-meta.xml │ │ ├── Mocha.cls │ │ ├── Mocha.cls-meta.xml │ │ ├── Soy.cls │ │ └── Soy.cls-meta.xml │ ├── DmlFactory.cls │ ├── DmlFactory.cls-meta.xml │ ├── FactoryMethod │ │ ├── ChicagoPizzaStore.cls │ │ ├── ChicagoPizzaStore.cls-meta.xml │ │ ├── ChicagoPizzaStore_Tests.cls │ │ ├── ChicagoPizzaStore_Tests.cls-meta.xml │ │ ├── ChicagoStyleCheese.cls │ │ ├── ChicagoStyleCheese.cls-meta.xml │ │ ├── ChicagoStylePepperoni.cls │ │ ├── ChicagoStylePepperoni.cls-meta.xml │ │ ├── ChicagoStyleSausage.cls │ │ ├── ChicagoStyleSausage.cls-meta.xml │ │ ├── DependentPizzaStore.cls │ │ ├── DependentPizzaStore.cls-meta.xml │ │ ├── NewYorkPizzaStore.cls │ │ ├── NewYorkPizzaStore.cls-meta.xml │ │ ├── NewYorkPizzaStore_Tests.cls │ │ ├── NewYorkPizzaStore_Tests.cls-meta.xml │ │ ├── NewYorkStyleCheese.cls │ │ ├── NewYorkStyleCheese.cls-meta.xml │ │ ├── NewYorkStylePepperoni.cls │ │ ├── NewYorkStylePepperoni.cls-meta.xml │ │ ├── NewYorkStyleSausage.cls │ │ ├── NewYorkStyleSausage.cls-meta.xml │ │ ├── PizzaStore.cls │ │ └── PizzaStore.cls-meta.xml │ ├── IDML.cls │ ├── IDML.cls-meta.xml │ ├── ISelector.cls │ ├── ISelector.cls-meta.xml │ ├── OO_Fundamentals │ │ ├── CardFactory.cls │ │ ├── CardFactory.cls-meta.xml │ │ ├── CardFactory_Tests.cls │ │ ├── CardFactory_Tests.cls-meta.xml │ │ ├── CardType.cls │ │ ├── CardType.cls-meta.xml │ │ ├── CreditCard.cls │ │ ├── CreditCard.cls-meta.xml │ │ ├── Discover.cls │ │ ├── Discover.cls-meta.xml │ │ ├── MasterCard.cls │ │ ├── MasterCard.cls-meta.xml │ │ ├── PaymentController1.cls │ │ ├── PaymentController1.cls-meta.xml │ │ ├── PaymentController2.cls │ │ ├── PaymentController2.cls-meta.xml │ │ ├── PaymentController2_Tests.cls │ │ ├── PaymentController2_Tests.cls-meta.xml │ │ ├── PaymentService.cls │ │ ├── PaymentService.cls-meta.xml │ │ ├── Visa.cls │ │ └── Visa.cls-meta.xml │ ├── Observer │ │ ├── IObserver.cls │ │ ├── IObserver.cls-meta.xml │ │ ├── ISubject.cls │ │ ├── ISubject.cls-meta.xml │ │ ├── ObserverPatternController.cls │ │ ├── ObserverPatternController.cls-meta.xml │ │ ├── ProductCatalogTriggerHandler.cls │ │ ├── ProductCatalogTriggerHandler.cls-meta.xml │ │ ├── ProductObserver.cls │ │ ├── ProductObserver.cls-meta.xml │ │ ├── ProductSubject.cls │ │ └── ProductSubject.cls-meta.xml │ ├── Refactoring │ │ ├── Athlete.cls │ │ ├── Athlete.cls-meta.xml │ │ ├── Customer2.cls │ │ ├── Customer2.cls-meta.xml │ │ ├── composing_methods │ │ │ ├── ExtractMethodSample.cls │ │ │ ├── ExtractMethodSample.cls-meta.xml │ │ │ ├── ReplaceTempWithQuerySample.cls │ │ │ └── ReplaceTempWithQuerySample.cls-meta.xml │ │ ├── replaceConditionalWithStrategy │ │ │ ├── CalculateSalary1.cls │ │ │ ├── CalculateSalary1.cls-meta.xml │ │ │ ├── Employee1.cls │ │ │ ├── Employee1.cls-meta.xml │ │ │ └── aura │ │ │ │ └── Pay │ │ │ │ ├── Pay.intf │ │ │ │ └── Pay.intf-meta.xml │ │ └── simplify_ conditionals │ │ │ ├── ReplaceConditionalWithGuardClause.cls │ │ │ └── ReplaceConditionalWithGuardClause.cls-meta.xml │ ├── ReflectiveFactory │ │ ├── IPizzaFactory.cls │ │ ├── IPizzaFactory.cls-meta.xml │ │ ├── ReflectivePizzaFactory.cls │ │ ├── ReflectivePizzaFactory.cls-meta.xml │ │ ├── ReflectivePizzaFactory_Tests.cls │ │ └── ReflectivePizzaFactory_Tests.cls-meta.xml │ ├── Selector.cls │ ├── Selector.cls-meta.xml │ ├── SelectorFacory_Tests.cls │ ├── SelectorFacory_Tests.cls-meta.xml │ ├── SelectorFactory.cls │ ├── SelectorFactory.cls-meta.xml │ ├── SelectorMock.cls │ ├── SelectorMock.cls-meta.xml │ ├── SimpleFactory │ │ ├── CheesePizza.cls │ │ ├── CheesePizza.cls-meta.xml │ │ ├── PepperoniPizza.cls │ │ ├── PepperoniPizza.cls-meta.xml │ │ ├── Pizza.cls │ │ ├── Pizza.cls-meta.xml │ │ ├── SimplePizzaFactory.cls │ │ ├── SimplePizzaFactory.cls-meta.xml │ │ ├── SimplePizzaFactory_Tests.cls │ │ ├── SimplePizzaFactory_Tests.cls-meta.xml │ │ ├── SimplePizzaStore.cls │ │ ├── SimplePizzaStore.cls-meta.xml │ │ ├── SimplePizzaStore_Tests.cls │ │ ├── SimplePizzaStore_Tests.cls-meta.xml │ │ ├── TacoPizza.cls │ │ └── TacoPizza.cls-meta.xml │ ├── State │ │ ├── CaseHelper.cls │ │ ├── CaseHelper.cls-meta.xml │ │ ├── CloseCaseStatehandler.cls │ │ ├── CloseCaseStatehandler.cls-meta.xml │ │ ├── ICaseState.cls │ │ ├── ICaseState.cls-meta.xml │ │ ├── InProcessCaseStateHandler.cls │ │ ├── InProcessCaseStateHandler.cls-meta.xml │ │ ├── NewCaseState.cls │ │ ├── NewCaseState.cls-meta.xml │ │ ├── OpenCaseStateHandler.cls │ │ └── OpenCaseStateHandler.cls-meta.xml │ ├── TestingUtils.cls │ ├── TestingUtils.cls-meta.xml │ ├── TriggerHandler.cls │ ├── TriggerHandler.cls-meta.xml │ ├── TriggerHandler_Tests.cls │ ├── TriggerHandler_Tests.cls-meta.xml │ ├── Visitor │ │ ├── IMusicVisitor.cls │ │ ├── IMusicVisitor.cls-meta.xml │ │ ├── Music.cls │ │ ├── Music.cls-meta.xml │ │ ├── MusicPriceCalculator.cls │ │ ├── MusicPriceCalculator.cls-meta.xml │ │ ├── MusicVisitor.cls │ │ ├── MusicVisitor.cls-meta.xml │ │ ├── VisitorTest.cls │ │ └── VisitorTest.cls-meta.xml │ ├── problem_solving_patterns │ │ ├── FrequencyCounter.cls │ │ ├── FrequencyCounter.cls-meta.xml │ │ ├── FrequencyCounter_Tests.cls │ │ └── FrequencyCounter_Tests.cls-meta.xml │ └── singleton │ │ ├── HotLeadSingleton.cls │ │ ├── HotLeadSingleton.cls-meta.xml │ │ ├── HotLeadSingleton_Tests.cls │ │ ├── HotLeadSingleton_Tests.cls-meta.xml │ │ ├── IFeatureToggle.cls │ │ └── IFeatureToggle.cls-meta.xml │ ├── contentassets │ ├── codey.asset │ └── codey.asset-meta.xml │ ├── customMetadata │ ├── Chicago_Pizza.cheese.md-meta.xml │ ├── Chicago_Pizza.pepperoni.md-meta.xml │ ├── Chicago_Pizza.sausage.md-meta.xml │ ├── Pizza.cheese.md-meta.xml │ ├── Pizza.pepperoni.md-meta.xml │ └── Pizza.taco.md-meta.xml │ ├── flexipages │ ├── Design_Patterns_UtilityBar.flexipage-meta.xml │ └── Observer_Pattern.flexipage-meta.xml │ ├── layouts │ ├── Chicago_Pizza__mdt-Chicago Pizza Layout.layout-meta.xml │ ├── Lead-Lead %28Marketing%29 Layout.layout-meta.xml │ ├── Lead-Lead %28Sales%29 Layout.layout-meta.xml │ ├── Lead-Lead %28Support%29 Layout.layout-meta.xml │ ├── Lead-Lead Layout.layout-meta.xml │ ├── Pizza__mdt-Pizza Layout.layout-meta.xml │ ├── Product_Catalog__c-Product Catelog Layout.layout-meta.xml │ └── Product_Notification__c-Product Notification Layout.layout-meta.xml │ ├── lwc │ ├── .eslintrc.json │ └── productSubscribe │ │ ├── productSubscribe.html │ │ ├── productSubscribe.js │ │ └── productSubscribe.js-meta.xml │ ├── objectTranslations │ └── Product_Catalog__c-en_US │ │ ├── Is_Available__c.fieldTranslation-meta.xml │ │ ├── Product_Catalog__c-en_US.objectTranslation-meta.xml │ │ └── Product_Name__c.fieldTranslation-meta.xml │ ├── objects │ ├── Account │ │ └── fields │ │ │ └── Admin_Only__c.field-meta.xml │ ├── Chicago_Pizza__mdt │ │ ├── Chicago_Pizza__mdt.object-meta.xml │ │ └── fields │ │ │ └── Class_To_Instantiate__c.field-meta.xml │ ├── Lead │ │ └── fields │ │ │ └── Is_Hot__c.field-meta.xml │ ├── Pizza__mdt │ │ ├── Pizza__mdt.object-meta.xml │ │ └── fields │ │ │ └── Class_To_Instantiate__c.field-meta.xml │ ├── Product_Catalog__c │ │ ├── Product_Catalog__c.object-meta.xml │ │ ├── fields │ │ │ ├── Is_Available__c.field-meta.xml │ │ │ └── Product_Name__c.field-meta.xml │ │ └── listViews │ │ │ └── All.listView-meta.xml │ └── Product_Notification__c │ │ ├── Product_Notification__c.object-meta.xml │ │ ├── fields │ │ ├── Email_Address__c.field-meta.xml │ │ ├── Product_Catelog__c.field-meta.xml │ │ ├── Send_Email__c.field-meta.xml │ │ └── Subscribe__c.field-meta.xml │ │ └── listViews │ │ └── All.listView-meta.xml │ ├── permissionsets │ └── Design_Patterns.permissionset-meta.xml │ ├── tabs │ ├── Observer_Pattern.tab-meta.xml │ ├── Product_Catalog__c.tab-meta.xml │ └── Product_Notification__c.tab-meta.xml │ └── triggers │ ├── ProductCatalog.trigger │ └── ProductCatalog.trigger-meta.xml ├── jest.config.js ├── manifest └── package.xml ├── package-lock.json ├── package.json ├── recipes-logo.png ├── scripts ├── apex │ └── hello.apex └── soql │ └── account.soql └── sfdx-project.json /.eslintignore: -------------------------------------------------------------------------------- 1 | **/lwc/**/*.css 2 | **/lwc/**/*.html 3 | **/lwc/**/*.json 4 | **/lwc/**/*.svg 5 | **/lwc/**/*.xml 6 | **/aura/**/*.auradoc 7 | **/aura/**/*.cmp 8 | **/aura/**/*.css 9 | **/aura/**/*.design 10 | **/aura/**/*.evt 11 | **/aura/**/*.json 12 | **/aura/**/*.svg 13 | **/aura/**/*.tokens 14 | **/aura/**/*.xml 15 | **/aura/**/*.app 16 | .sfdx 17 | -------------------------------------------------------------------------------- /.forceignore: -------------------------------------------------------------------------------- 1 | # .forceignore v2 2 | # List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status 3 | # More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm 4 | 5 | 6 | package.xml 7 | 8 | # LWC configuration files 9 | **/jsconfig.json 10 | **/.eslintrc.json 11 | 12 | # LWC Jest 13 | **/__tests__/** 14 | 15 | **profiles 16 | force-app/main/default/profile 17 | force-app/main/default/profiles 18 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Design Patterns In Apex CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ master ] 10 | paths-ignore: 11 | - 'sfdx-project.json' 12 | - 'README.md' 13 | 14 | pull_request: 15 | branches: [ master ] 16 | paths-ignore: 17 | - 'sfdx-project.json' 18 | - 'README.md' 19 | 20 | # Allows you to run this workflow manually from the Actions tab 21 | workflow_dispatch: 22 | 23 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 24 | jobs: 25 | # This workflow contains a single job called "build" 26 | build: 27 | runs-on: ubuntu-latest 28 | steps: 29 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 30 | - uses: actions/checkout@v2 31 | with: 32 | ref: ${{ github.ref }} 33 | fetch-depth: 0 34 | if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event_name == 'push' 35 | - uses: actions/setup-node@v1 36 | with: 37 | node-version: '>=14' 38 | check-latest: true 39 | 40 | - name: Install Salesforce CLI 41 | run: | 42 | npm install sfdx-cli 43 | node_modules/sfdx-cli/bin/run --version 44 | node_modules/sfdx-cli/bin/run plugins --core 45 | 46 | - name: 'Populate auth file with SFDX_URL secret' 47 | shell: bash 48 | run: 'echo ${{ secrets.SFDX_AUTH_URL}} > SFDX_QA' 49 | 50 | - name: 'Authenticate Dev Hub' 51 | run: node_modules/sfdx-cli/bin/run force:auth:sfdxurl:store -f SFDX_QA -s -a devhub -d 52 | 53 | - name: 'Create Scratch Org' 54 | run: node_modules/sfdx-cli/bin/run force:org:create -f config/project-scratch-def.json -a scratch-org -s -d 1 55 | 56 | - name: 'Push source to scratch org' 57 | run: node_modules/sfdx-cli/bin/run force:source:push 58 | 59 | - name: Run Apex test 60 | run: 61 | node_modules/sfdx-cli/bin/run force:apex:test:run --resultformat tap --codecoverage -c -r human -w 20 62 | 63 | - name: Delete Scratch Org 64 | run: 65 | node_modules/sfdx-cli/bin/run force:org:delete -p 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used for Git repositories to specify intentionally untracked files that Git should ignore. 2 | # If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore 3 | # For useful gitignore templates see: https://github.com/github/gitignore 4 | 5 | # Salesforce cache 6 | .sfdx/ 7 | .localdevserver/ 8 | 9 | # LWC VSCode autocomplete 10 | **/lwc/jsconfig.json 11 | 12 | # LWC Jest coverage reports 13 | coverage/ 14 | 15 | # Logs 16 | logs 17 | *.log 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | # Dependency directories 23 | node_modules/ 24 | 25 | # Eslint cache 26 | .eslintcache 27 | 28 | # MacOS system files 29 | .DS_Store 30 | 31 | # Windows system files 32 | Thumbs.db 33 | ehthumbs.db 34 | [Dd]esktop.ini 35 | $RECYCLE.BIN/ 36 | 37 | # Local environment variables 38 | .env 39 | 40 | #local IDE stuff 41 | .idea 42 | Design_Patterns_In_Apex.iml 43 | node_modules 44 | /IlluminatedCloud/ 45 | /force-app/main/default/profiles/ 46 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running prettier 2 | # More information: https://prettier.io/docs/en/ignore.html 3 | # 4 | 5 | **/staticresources/** 6 | .localdevserver 7 | .sfdx 8 | .vscode 9 | 10 | coverage/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "overrides": [ 4 | { 5 | "files": "**/lwc/**/*.html", 6 | "options": { "parser": "lwc" } 7 | }, 8 | { 9 | "files": "*.{cmp,page,component}", 10 | "options": { "parser": "html" } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "salesforce.salesforcedx-vscode", 4 | "redhat.vscode-xml", 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode", 7 | "financialforce.lana" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Apex Replay Debugger", 9 | "type": "apex-replay", 10 | "request": "launch", 11 | "logFile": "${command:AskForLogFileName}", 12 | "stopOnEntry": true, 13 | "trace": true 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "**/node_modules": true, 4 | "**/bower_components": true, 5 | "**/.sfdx": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Design Patterns In The Apex Programming Language 3 |  4 | 5 | 6 |  7 | 8 | Few things have helped me more in my career than understanding how you use the Object Oriented Features of Apex to write 9 | code that is flexible, testable and maintainable. I have done quite a few YouTube videos on this topic and have a lot 10 | more to do. I was not happy with my previous Repo and its organization. This is an attempt of my part to give back to 11 | the community with repo that offers implementations of all the class Gang of Four patterns in Apex. I grant you that my 12 | implementations will often be trivial and contrived. But I hope that helps make the underlying ideas easy to understand. 13 | 14 | If I have also published a video about the pattern I have added a link to in the heading. 15 | 16 | ## Table of Contents 17 | ## Creational Patterns 18 | 19 | Design patterns that are concerned with the creation of objects 20 | 21 | #### [The Simple Factory Pattern](https://github.com/bdJohnson72/Design-Patterns-In-Apex/tree/master/force-app/main/default/classes/SimpleFactory) 22 | 23 | - One of my every day go to patterns. Code is based on Head First Design Patterns 24 | - [UML diagram](https://github.com/bdJohnson72/Design-Patterns-In-Apex/blob/master/UML/simple%20factory/Simple%20Factory.png) 25 | 26 | #### [The Reflective Factory](https://github.com/bdJohnson72/Design-Patterns-In-Apex/tree/master/force-app/main/default/classes/ReflectiveFactory) 27 | 28 | -[Watch on YouTube](https://www.youtube.com/watch?v=zNuOWwIl-xo&t=1322s) 29 | 30 | This can be a bit more involved but is also the ultimate in closed for modification but open for extension. By making 31 | our factory reflective we never need to update it when we add new concrete types. 32 | 33 | - Unique to Apex and Salesforce. Refactor the simple factory pattern with custom metadata. Wow your colleagues with the 34 | Type class. One of my favorite Apex party tricks. 35 | 36 | #### [The Factory Method](https://github.com/bdJohnson72/Design-Patterns-In-Apex/tree/master/force-app/main/default/classes/SimpleFactory) 37 | 38 | -[Watch on YouTube](https://www.youtube.com/watch?v=xC86Aof8bV4&t=10s) 39 | 40 | Another implementation using the pizza example from Head First Design Patterns. What do we do if we want our Pizza Store 41 | to access different factories? 42 | 43 | #### [The Singleton](https://github.com/bdJohnson72/Design-Patterns-In-Apex/tree/master/force-app/main/default/classes/singleton) 44 | 45 | -[Watch on YouTube](https://www.youtube.com/watch?v=f4n0oyYcvtw) 46 | 47 | An easy pattern to understand and always useful for keeping your governer limits under control 48 | 49 | ## Behavioral Design Patterns 50 | 51 | These patterns are all about the communication between objects. How can objects cooperate to perform tasks. 52 | 53 | ### [The Observer](https://github.com/bdJohnson72/Design-Patterns-In-Apex/tree/master/force-app/main/default/classes/Observer) 54 | -[Watch on YouTube](https://www.youtube.com/watch?v=w-yKRDCuDPI) 55 | 56 | When one object changes state we want to notify all the depenents. But we want to publisher and subscribers to be 57 | loosely coupled. 58 | 59 | ##Refactoring 60 | I have begun to add some of the examples from [Martin Fowler's Refactoring](amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Signature/dp/0134757599/ref=sr_1_1?keywords=refactoring&qid=1655299896&s=books&sprefix=refacto%2Cstripbooks%2C68&sr=1-1) 61 | and [Joshua Kerievsky's Refactoring to Patterns](amazon.com/Refactoring-Patterns-Joshua-Kerievsky/dp/0321213351) 62 | 63 | - [Composing Methods](github.com/bdJohnson72/Design-Patterns-In-Apex/tree/master/force-app/main/default/classes/Refactoring/composing_methods) 64 | - [Simplifying Conditionals](github.com/bdJohnson72/Design-Patterns-In-Apex/tree/master/force-app/main/default/classes/Refactoring/simplify_%20conditionals) 65 | 66 | ## Read All About It 67 | 68 | Nothing in this repo would be possible without the books below. And most of the implementations will be based on ones 69 | found in these books. If they are not on your shelf they should be. 70 | 71 | - [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612/ref=sr_1_1?dchild=1&keywords=Design+Patterns&qid=1631976412&sr=8-1) 72 | - [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm) 73 | - [Apex Design Patterns](https://www.amazon.com/Apex-Design-Patterns-Jitendra-Zaa/dp/178217365X/ref=sr_1_3?dchild=1&keywords=apex+design+patterns&qid=1631976687&sr=8-3) 74 | -------------------------------------------------------------------------------- /UML/Builder/Builder.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/class-diagram 3 | 4 | Director o-->Builder 5 | Builder <|-- ConcreteBuilder 6 | ConcreteBuilder --> Product 7 | 8 | class Product 9 | 10 | class ConcreteBuilder{ 11 | buildPart() 12 | getResult() 13 | } 14 | 15 | Interface Builder{ 16 | buildPart() 17 | } 18 | 19 | class Director{ 20 | Construct() 21 | } 22 | 23 | @enduml -------------------------------------------------------------------------------- /UML/Observer/Observer.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/class-diagram 3 | 4 | ISubject <|-- ProductSubject 5 | 6 | IObserver <|-- ProductObserver 7 | ProductSubject --o ProductObserver 8 | interface ISubject { 9 | subscribe() 10 | unsubscribe() 11 | notifyObservers() 12 | } 13 | interface IObserver { 14 | notify() 15 | } 16 | 17 | class ProductSubject{ 18 | +observerCollection 19 | } 20 | 21 | 22 | note "For observer in observer collection observer.notify()" as N1 23 | N1 .. ProductSubject 24 | 25 | 26 | 27 | 28 | @enduml -------------------------------------------------------------------------------- /UML/StatePattern/StatePattern.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/class-diagram 3 | Context *--> State 4 | State <|-- State1 5 | State <|-- State2 6 | State <|-- State3 7 | 8 | 9 | Interface State{ 10 | + doAction() 11 | } 12 | class Context{ 13 | -state state 14 | } 15 | class State1{ 16 | + doAction() 17 | } 18 | class State2{ 19 | + doAction() 20 | } 21 | class State3{ 22 | + doAction() 23 | } 24 | @enduml -------------------------------------------------------------------------------- /UML/factory method/DependentPizzaStore.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/class-diagram 3 | PizzaStore --> NewYorkStyleCheese 4 | PizzaStore --> NewYorkStyleSausage 5 | PizzaStore --> NewYorkStylePepperoni 6 | PizzaStore --> ChicagoStyleCheese 7 | PizzaStore --> ChicagoStyleSausage 8 | PizzaStore --> ChicagoStylePepperoni 9 | 10 | class PizzaStore 11 | 12 | @enduml -------------------------------------------------------------------------------- /UML/factory method/factorymethod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bdJohnson72/Design-Patterns-In-Apex/aad3c7a402f3e39a1d45c747b5a8a8acc00e4198/UML/factory method/factorymethod.png -------------------------------------------------------------------------------- /UML/factory method/factorymethod.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/class-diagram 3 | 4 | abstract class PizzaStore{ 5 | +orderPizza(String pizzaType) 6 | {abstract} createPizza() 7 | } 8 | abstract class Pizza{ 9 | +prepare() 10 | +bake() 11 | +cut() 12 | +box() 13 | } 14 | 15 | class NewYorkPizzaStore{ 16 | +createPizza(String type) 17 | } 18 | 19 | class ChicagoPizzaStore{ 20 | +createPizza(String type) 21 | } 22 | 23 | class NewYorkStyleCheese{} 24 | class NewYorkStyleSausage{} 25 | class NewYorkStylePepperoni{} 26 | class ChicagoStyleCheese{} 27 | class ChicagoStyleSausage{} 28 | class ChicagoStylePepperoni{} 29 | 30 | PizzaStore -> Pizza 31 | 32 | PizzaStore <|-- NewYorkPizzaStore 33 | PizzaStore <|-- ChicagoPizzaStore 34 | Pizza <|-- NewYorkStyleSausage 35 | Pizza <|-- NewYorkStyleCheese 36 | Pizza <|-- NewYorkStylePepperoni 37 | Pizza <|-- ChicagoStylePepperoni 38 | Pizza <|-- ChicagoStyleCheese 39 | Pizza <|-- ChicagoStyleSausage 40 | 41 | @enduml -------------------------------------------------------------------------------- /UML/interpreter.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/class-diagram 3 | 4 | 5 | class Client{} 6 | class Context{} 7 | abstract class Expression{ 8 | interpret(Context) 9 | } 10 | class TerminalExpressions{} 11 | class NonTerminalExpression{} 12 | 13 | Client -> Context 14 | Client -> Expression 15 | Expression <|-- TerminalExpressions 16 | Expression <|-- NonTerminalExpression 17 | @enduml -------------------------------------------------------------------------------- /UML/simple factory/Simple Factory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bdJohnson72/Design-Patterns-In-Apex/aad3c7a402f3e39a1d45c747b5a8a8acc00e4198/UML/simple factory/Simple Factory.png -------------------------------------------------------------------------------- /UML/simple factory/Simple Factory.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/class-diagram 3 | 4 | 5 | SimplePizzaStore -> SimplePizzaFactory 6 | SimplePizzaFactory -> Pizza 7 | Pizza <|-- CheesePizza 8 | Pizza <|-- TacoPizza 9 | Pizza <|-- PepperoniPizza 10 | 11 | class SimplePizzaStore{ 12 | +orderPizza(String type) 13 | } 14 | 15 | class SimplePizzaFactory{ 16 | +createPizza(String type) 17 | } 18 | Abstract class Pizza { 19 | +prepare() 20 | +bake() 21 | +cut() 22 | +box() 23 | } 24 | 25 | 26 | 27 | @enduml -------------------------------------------------------------------------------- /UML/stategy/strategy.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'https://plantuml.com/class-diagram 3 | context *--> Strategy : composed 4 | Strategy <|-- Strategy1 5 | Strategy <|-- Strategy2 6 | Strategy <|-- Strategy3 7 | 8 | 9 | class context{ 10 | + useStrategy() 11 | } 12 | 13 | interface Strategy{ 14 | +doThing() 15 | } 16 | 17 | class Strategy1 18 | class Strategy2 19 | class Strategy3 20 | 21 | @enduml -------------------------------------------------------------------------------- /config/project-scratch-def.json: -------------------------------------------------------------------------------- 1 | { 2 | "orgName": "Demo company", 3 | "edition": "Developer", 4 | "features": ["EnableSetPasswordInApi"], 5 | "settings": { 6 | "lightningExperienceSettings": { 7 | "enableS1DesktopEnabled": true 8 | }, 9 | "mobileSettings": { 10 | "enableS1EncryptedStoragePref2": false 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /force-app/main/default/appMenus/AppSwitcher.appMenu-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | standard__Platform 5 | CustomApplication 6 | 7 | 8 | standard__Sales 9 | CustomApplication 10 | 11 | 12 | standard__Service 13 | CustomApplication 14 | 15 | 16 | standard__Marketing 17 | CustomApplication 18 | 19 | 20 | standard__ServiceConsole 21 | CustomApplication 22 | 23 | 24 | standard__AppLauncher 25 | CustomApplication 26 | 27 | 28 | standard__Community 29 | CustomApplication 30 | 31 | 32 | standard__Sites 33 | CustomApplication 34 | 35 | 36 | standard__Chatter 37 | CustomApplication 38 | 39 | 40 | standard__Content 41 | CustomApplication 42 | 43 | 44 | standard__LightningSales 45 | CustomApplication 46 | 47 | 48 | standard__AllTabSet 49 | CustomApplication 50 | 51 | 52 | CPQIntegrationUserApp 53 | ConnectedApp 54 | 55 | 56 | standard__LightningBolt 57 | CustomApplication 58 | 59 | 60 | standard__SalesforceCMS 61 | CustomApplication 62 | 63 | 64 | Design_Patterns 65 | CustomApplication 66 | 67 | 68 | -------------------------------------------------------------------------------- /force-app/main/default/applications/Design_Patterns.app-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #37B407 5 | codey 6 | 1 7 | false 8 | 9 | Small 10 | Large 11 | false 12 | false 13 | Design Patterns 14 | Standard 15 | Observer_Pattern 16 | Lightning 17 | Design_Patterns_UtilityBar 18 | 19 | -------------------------------------------------------------------------------- /force-app/main/default/aura/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@salesforce/eslint-plugin-aura"], 3 | "extends": ["plugin:@salesforce/eslint-plugin-aura/recommended", "prettier"], 4 | "rules": { 5 | "vars-on-top": "off", 6 | "no-unused-expressions": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /force-app/main/default/classes/ApexSandbox-io/IsSorted_Test.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 8/20/2022. 3 | */ 4 | 5 | @IsTest 6 | private class IsSorted_Test { 7 | private static final List ASCENDING = new List{ 8 | 1, 2, 3, 4, 5 9 | }; 10 | private static final List NOT_SORTED = new List{ 11 | 1, 2, 3, 5, 4 12 | }; 13 | private static final List DESCENDING = new List{ 14 | 5, 4, 3, 2, 1 15 | }; 16 | 17 | @IsTest 18 | static void testBehavior() { 19 | System.assert(true == SortedList.isSorted(ASCENDING)); 20 | System.assert(false == SortedList.isSorted(NOT_SORTED)); 21 | System.assert(true == SortedList.isSorted(DESCENDING)); 22 | } 23 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/ApexSandbox-io/IsSorted_Test.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/ApexSandbox-io/SecondLargest.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 8/20/2022. 3 | */ 4 | 5 | public with sharing class SecondLargest { 6 | public static Integer onePass(List numbers) { 7 | Integer largest = -2147483647 - 1; 8 | Integer seccondLargest = -2147483647 - 1 ; 9 | for (Integer i = 0; i < numbers.size(); i++) { 10 | if (numbers[i] > largest) { 11 | seccondLargest = largest; 12 | largest = numbers[i]; 13 | } else if (numbers[i] > seccondLargest && numbers[i] != largest) { 14 | seccondLargest = numbers[i]; 15 | } 16 | } 17 | return seccondLargest; 18 | } 19 | 20 | public static Integer sortNums1(List nums) { 21 | Set intSet = new Set(nums); 22 | List integers = new List(intSet); 23 | integers.sort(); 24 | return integers[intSet.size() - 2]; 25 | } 26 | 27 | 28 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/ApexSandbox-io/SecondLargest.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/ApexSandbox-io/SecondLargest_Tests.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 8/20/2022. 3 | */ 4 | 5 | @IsTest 6 | private class SecondLargest_Tests { 7 | private static final List NUMS = new List{ 8 | 1, 4, 78, -10, -300, 100, 100, 100, 99, 50, 41 9 | }; 10 | private static final List ALL_NEGATIVES = new List{ 11 | -1, -4, -78, -10, -300, -100, -100, -100, -99, -50, -41 12 | }; 13 | @IsTest 14 | static void onePassTest() { 15 | Double cpuStart = Limits.getCpuTime(); 16 | Integer result = SecondLargest.onePass(NUMS); 17 | Double cpuEnd = Limits.getCpuTime(); 18 | System.debug(cpuEnd - cpuStart); 19 | System.assert(result == 99); 20 | result = SecondLargest.onePass(ALL_NEGATIVES); 21 | System.debug(result); 22 | System.assert(result == -4); 23 | } 24 | 25 | @IsTest 26 | static void sortedFirst() { 27 | Double cpuStart = Limits.getCpuTime(); 28 | Integer result = SecondLargest.sortNums1(NUMS); 29 | Double cpuEnd = Limits.getCpuTime(); 30 | System.debug(cpuEnd - cpuStart); 31 | System.assert(result == 99); 32 | System.assert(-4 == SecondLargest.sortNums1(ALL_NEGATIVES)); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/ApexSandbox-io/SecondLargest_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/ApexSandbox-io/SortedList.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 8/20/2022. 3 | */ 4 | 5 | public with sharing class SortedList { 6 | 7 | public static Boolean isSorted(List numbers) { 8 | if (numbers.size() < 2) { 9 | return true; 10 | } 11 | Integer left = 0; 12 | Integer right = numbers.size() - 1; 13 | if (numbers[left] < numbers[right]) { 14 | //check acsending 15 | while (left < right) { 16 | if (numbers[left + 1] < numbers[left]) { 17 | return false; 18 | } 19 | left++; 20 | } 21 | } 22 | if (numbers[left] > numbers[right]) { 23 | //check descending 24 | while (left < right) { 25 | if (numbers[left + 1] > numbers[left]) { 26 | return false; 27 | } 28 | left++; 29 | } 30 | } 31 | if (numbers[left] == numbers[right]) { 32 | //the list needs to be same from left to right 33 | while (left < right) { 34 | if (numbers[left + 1] != numbers[left]) { 35 | return false; 36 | } 37 | left++; 38 | } 39 | 40 | } 41 | return true; 42 | 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/ApexSandbox-io/SortedList.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/BlogFramework.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by bjohnson on 1/20/23. 3 | */ 4 | 5 | public with sharing class BlogFramework extends WebFramework { 6 | 7 | public BlogFramework(ITheme theme){ 8 | super(theme); 9 | } 10 | 11 | public override void capability() { 12 | System.debug('This app does not support menu and it is not styled'); 13 | } 14 | 15 | 16 | public override void showMenuBody() { 17 | theme.styleContent(); 18 | } 19 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/BlogFramework.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/CMSFramework.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by bjohnson on 1/20/23. 3 | */ 4 | 5 | public with sharing class CMSFramework extends WebFramework { 6 | 7 | public CMSFramework(ITheme theme) { 8 | super(theme); 9 | } 10 | 11 | public override void capability() { 12 | System.debug('This is a CMS Framework and supports workflow to ' + 13 | 'publish public content'); 14 | } 15 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/CMSFramework.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/ITheme.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by bjohnson on 1/19/23. 3 | */ 4 | 5 | public interface ITheme { 6 | 7 | void styleHeader(); 8 | void styleFooter(); 9 | void styleMenu(); 10 | void styleContent(); 11 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/ITheme.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/Theme_WhiteBlue.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by bjohnson on 1/19/23. 3 | */ 4 | 5 | public with sharing class Theme_WhiteBlue implements ITheme { 6 | 7 | 8 | public void styleHeader() { 9 | System.debug('Header font size : 16 px'); 10 | System.debug('Background: Blue, Color: black'); 11 | } 12 | 13 | public void styleFooter() { 14 | System.debug('Footer - font size : 14 px'); 15 | System.debug('Background : White, Color: blue'); 16 | } 17 | 18 | public void styleMenu() { 19 | System.debug('Menu - font size : 12 px'); 20 | System.debug('Background : blue, Color: white'); 21 | } 22 | 23 | public void styleContent() { 24 | System.debug('Content - font size : 16px'); 25 | System.debug('Background : white, Color: blue'); 26 | } 27 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/Theme_WhiteBlue.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/Theme_WhiteGreen.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by bjohnson on 1/19/23. 3 | */ 4 | 5 | public with sharing class Theme_WhiteGreen implements ITheme { 6 | 7 | 8 | public void styleHeader() { 9 | System.debug('Header - font size : 16px'); 10 | System.debug('Background : Green, Color : Black'); 11 | } 12 | 13 | public void styleFooter() { 14 | System.debug('Footer - font size : 14px'); 15 | System.debug('Background : White, Color : Green'); 16 | } 17 | 18 | public void styleMenu() { 19 | System.debug('Footer, Font size : 14px'); 20 | System.debug('Background : White, Color : Green'); 21 | } 22 | 23 | public void styleContent() { 24 | System.debug('menu - font size : 14px'); 25 | System.debug('Background : White, Color : Green'); 26 | } 27 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/Theme_WhiteGreen.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/WebFramework.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by bjohnson on 1/20/23. 3 | */ 4 | 5 | public with sharing abstract class WebFramework { 6 | 7 | protected ITheme theme; 8 | private String title; 9 | private String keyword; 10 | private String content; 11 | 12 | public WebFramework(ITheme theme){ 13 | this.theme = theme; 14 | } 15 | 16 | //concrete web framework must implement this 17 | public abstract void capability(); 18 | 19 | private void setProperties(String title, String keyword, String content){ 20 | this.title = title; 21 | this.keyword = keyword; 22 | this.content = content; 23 | } 24 | 25 | private void printTitle() { 26 | System.debug('Title: ' + this.title); 27 | } 28 | 29 | private void printKeyword() { 30 | system.debug('Keyword: ' + this.keyword); 31 | } 32 | 33 | public virtual void showHeader_Footer(){ 34 | this.theme.styleHeader(); 35 | this.theme.styleFooter(); 36 | } 37 | 38 | public virtual void showMenuBody(){ 39 | this.theme.styleMenu(); 40 | this.theme.styleContent(); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Bridge/WebFramework.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/IUserBuilder.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 9/23/2022. 3 | */ 4 | 5 | public interface IUserBuilder { 6 | 7 | void setFirstName(); 8 | void setLastName(); 9 | void setEmail(); 10 | void setUsername(); 11 | void setProfile(); 12 | void setEmailEncodingKey(); 13 | void setAlias(); 14 | void setLanguageKey(); 15 | void setLocalSIDKey(); 16 | void setTimeZone(); 17 | void setPermSets(); 18 | User getUser(); 19 | 20 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/IUserBuilder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/NutritionFacts_Builder.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 9/3/2022. 3 | */ 4 | 5 | public with sharing class NutritionFacts_Builder { 6 | public class NutritionFactsException extends Exception { 7 | } 8 | private Integer servingSize; 9 | private Integer servings; 10 | private Integer calories; 11 | private Integer fat; 12 | private Integer sodium; 13 | private Integer carbohydrate; 14 | private Date startDate; 15 | private Date endDate; 16 | 17 | private NutritionFacts_Builder(NutritionFacts_Builder.Builder builder) { 18 | this.servingSize = builder.servingSize; 19 | this.servings = builder.servings; 20 | this.calories = builder.calories; 21 | this.fat = builder.fat; 22 | this.sodium = builder.sodium; 23 | this.carbohydrate = builder.carbohydrate; 24 | this.startDate = builder.startDate; 25 | } 26 | 27 | 28 | public class Builder { 29 | 30 | private Integer servingSize; 31 | private Integer servings; 32 | private Integer calories; 33 | private Integer fat; 34 | private Integer sodium; 35 | private Integer carbohydrate; 36 | private Date startDate; 37 | private Date endDate; 38 | 39 | public NutritionFacts_Builder.Builder servingSize(Integer servingSize) { 40 | this.servingSize = servingSize; 41 | return this; 42 | } 43 | 44 | public NutritionFacts_Builder.Builder servings(Integer servingSize) { 45 | this.servingSize = servingSize; 46 | return this; 47 | } 48 | 49 | Public NutritionFacts_Builder.Builder calories(Integer calories) { 50 | this.calories = calories; 51 | return this; 52 | } 53 | 54 | public NutritionFacts_Builder.Builder fat(Integer fat) { 55 | this.fat = fat; 56 | return this; 57 | } 58 | 59 | public NutritionFacts_Builder.Builder sodium(Integer sodium) { 60 | this.sodium = sodium; 61 | return this; 62 | } 63 | 64 | public NutritionFacts_Builder.Builder carbohydrates(Integer carbohydrate) { 65 | this.carbohydrate = carbohydrate; 66 | return this; 67 | } 68 | 69 | public NutritionFacts_Builder.Builder startDate(Date startDate) { 70 | this.startDate = startDate; 71 | return this; 72 | } 73 | 74 | private void validateDate() { 75 | if (startDate != null && endDate == null) { 76 | throw new NutritionFactsException('Must have an end date if a start date is present'); 77 | } 78 | } 79 | 80 | 81 | public NutritionFacts_Builder build() { 82 | validateDate(); 83 | return new NutritionFacts_Builder(this); 84 | } 85 | 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/NutritionFacts_Builder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/NutritionFacts_Fluent.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 9/3/2022. 3 | * @description example of the useing a fluent api to build our nutrion facts class. 4 | * The fluent API is a sort of facade over the class creation 5 | */ 6 | 7 | public with sharing class NutritionFacts_Fluent { 8 | private Integer servingSize; 9 | private Integer servings; 10 | private Integer calories; 11 | private Integer fat; 12 | private Integer sodium; 13 | private Integer carbohydrate; 14 | 15 | public NutritionFacts_Fluent servingSize(Integer servingSize) { 16 | this.servingSize = servingSize; 17 | return this; 18 | } 19 | 20 | public NutritionFacts_Fluent servings(Integer servings) { 21 | this.servings = servings; 22 | return this; 23 | } 24 | 25 | public NutritionFacts_Fluent calories(Integer calories) { 26 | this.calories = calories; 27 | return this; 28 | } 29 | 30 | public NutritionFacts_Fluent fat(Integer fat) { 31 | this.fat = fat; 32 | return this; 33 | } 34 | 35 | public NutritionFacts_Fluent sodium(Integer sodium) { 36 | this.sodium = sodium; 37 | return this; 38 | } 39 | 40 | public NutritionFacts_Fluent carbohydrate(Integer carbohydrate) { 41 | this.carbohydrate = carbohydrate; 42 | return this; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/NutritionFacts_Fluent.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/NutritionalFacts.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 7/19/2022. 3 | * Example taken from Effective Java. This clas will quickly get out of hand. 4 | * It works but is very hard to read. Very easy for the client to make is mistake with long s 5 | * sequence of parameters. Mix servings with calories 6 | */ 7 | 8 | public with sharing class NutritionalFacts { 9 | 10 | private Integer servingSize; 11 | private Integer servings; 12 | private Integer calories; 13 | private Integer fat; 14 | private Integer sodium; 15 | private Integer carbohydrate; 16 | 17 | public NutritionalFacts(Integer servingSize, Integer servings) { 18 | this(servingSize, servings, 0); 19 | } 20 | 21 | public NutritionalFacts(Integer servingSize, Integer servings, Integer calories) { 22 | this(servingSize, servings, calories, 0); 23 | } 24 | 25 | public NutritionalFacts(Integer servingSize, Integer servings, Integer calories, Integer fat) { 26 | this(servingSize, servings, calories, fat, 0); 27 | } 28 | 29 | public NutritionalFacts(Integer servingSize, Integer servings, Integer calories, Integer fat, Integer sodium){ 30 | this(servingSize, servings, calories, fat, sodium, 0); 31 | } 32 | 33 | public NutritionalFacts(Integer servingSize, Integer servings, Integer calories, Integer fat, Integer sodium, Integer carbohydrate) { 34 | this.servingSize = servingSize; 35 | this.servings = servings; 36 | this.calories = calories; 37 | this.fat = fat; 38 | this.sodium = sodium; 39 | this.carbohydrate = carbohydrate; 40 | } 41 | /* 42 | consider the use of static constructor methods instead. You can have as many as you want per class and 43 | The name of the the method will reveal your intent. 44 | */ 45 | public static NutritionalFacts taco() { 46 | return new NutritionalFacts(4, 1, 300, 10, 20); 47 | } 48 | 49 | public static NutritionalFacts cheeseBurger() { 50 | return new NutritionalFacts(16, 1, 600, 20, 10, 20); 51 | } 52 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/NutritionalFacts.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/UserBuilder.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 9/25/2022. 3 | */ 4 | 5 | public with sharing class UserBuilder { 6 | public with sharing class Admin implements IUserBuilder { 7 | public User user { get; private set; } 8 | 9 | public Admin() { 10 | this.user = new User(); 11 | } 12 | 13 | public void setFirstName() { 14 | user.FirstName = 'Test'; 15 | } 16 | 17 | public void setLastName() { 18 | user.LastName = 'Admin'; 19 | } 20 | 21 | public void setEmail() { 22 | user.Email = 'fake@fake.com'; 23 | } 24 | 25 | public void setUsername() { 26 | user.Username = 'testAdminUser@' + String.valueOf(Crypto.getRandomInteger()) + '.com'; 27 | } 28 | 29 | public void setProfile() { 30 | Profile profile = [SELECT Id FROM Profile WHERE Name = 'System Administrator' LIMIT 1]; 31 | user.ProfileId = profile.Id; 32 | } 33 | 34 | public void setEmailEncodingKey() { 35 | user.EmailEncodingKey = 'UTF-8'; 36 | } 37 | 38 | public void setAlias() { 39 | user.Alias = 'Admin'; 40 | } 41 | 42 | public void setLanguageKey() { 43 | user.LanguageLocaleKey = 'en_US'; 44 | } 45 | 46 | public void setLocalSIDKey() { 47 | user.LocaleSidKey = 'en_US'; 48 | } 49 | 50 | public void setTimeZone() { 51 | user.TimeZoneSidKey = 'America/Los_Angeles'; 52 | } 53 | 54 | public void setPermSets() { 55 | 56 | } 57 | 58 | 59 | public User getUser() { 60 | return user; 61 | } 62 | } 63 | 64 | public with sharing class Standard implements IUserBuilder { 65 | public User user { get; private set; } 66 | 67 | public Standard() { 68 | this.user = new User(); 69 | } 70 | 71 | 72 | public void setFirstName() { 73 | user.FirstName = 'Test'; 74 | } 75 | 76 | public void setLastName() { 77 | user.LastName = 'Standard User'; 78 | } 79 | 80 | public void setEmail() { 81 | user.Email = 'fake@fake.com'; 82 | } 83 | 84 | public void setUsername() { 85 | user.Username = 'testStandardUser@' + String.valueOf(Crypto.getRandomInteger()) + '.com'; 86 | } 87 | 88 | public void setProfile() { 89 | Profile profile = [SELECT Id, Name FROM Profile WHERE Name = 'Standard User']; 90 | user.ProfileId = profile.Id; 91 | } 92 | 93 | public void setEmailEncodingKey() { 94 | user.EmailEncodingKey = 'UTF-8'; 95 | } 96 | 97 | public void setAlias() { 98 | user.Alias = 'standard'; 99 | } 100 | 101 | public void setLanguageKey() { 102 | user.LanguageLocaleKey = 'en_US'; 103 | } 104 | 105 | public void setLocalSIDKey() { 106 | user.LocaleSidKey = 'en_US'; 107 | } 108 | 109 | public void setTimeZone() { 110 | user.TimeZoneSidKey = 'America/Los_Angeles'; 111 | } 112 | 113 | public void setPermSets() { 114 | 115 | } 116 | 117 | 118 | public User getUser() { 119 | return user; 120 | } 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/UserBuilder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/UserBuilder_Tests.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 9/25/2022. 3 | */ 4 | 5 | @IsTest 6 | private class UserBuilder_Tests { 7 | @IsTest 8 | static void itShouldUpdate() { 9 | User user = new UserDirector().build(new UserBuilder.Admin()); 10 | System.runAs(user) { 11 | Account account = new Account(Name = 'Test Account', Admin_Only__c = true); 12 | System.assert(Schema.SObjectType.Account.fields.Admin_Only__c.isAccessible() == true, 13 | 'This field should be accessible'); 14 | } 15 | } 16 | 17 | @IsTest 18 | static void itShouldNotUpdate() { 19 | User user = new UserDirector().build(new UserBuilder.Standard()); 20 | System.runAs(user) { 21 | Account account = new Account(Name = 'Test Account', Admin_Only__c = true); 22 | System.assert(Schema.SObjectType.Account.fields.Admin_Only__c.isAccessible() == false, 23 | 'This field should be accessible'); 24 | } 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/UserBuilder_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/UserDirector.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 9/23/2022. 3 | */ 4 | 5 | public with sharing class UserDirector { 6 | IUserBuilder userBuilder; 7 | 8 | 9 | public User build(IUserBuilder userBuilder) { 10 | this.userBuilder = userBuilder; 11 | userBuilder.setFirstName(); 12 | userBuilder.setLastName(); 13 | userBuilder.setUsername(); 14 | userBuilder.setEmail(); 15 | userBuilder.setAlias(); 16 | userBuilder.setEmailEncodingKey(); 17 | userBuilder.setLocalSIDKey(); 18 | userBuilder.setLanguageKey(); 19 | userBuilder.setProfile(); 20 | userBuilder.setTimeZone(); 21 | userBuilder.setPermSets(); 22 | return this.userBuilder.getUser(); 23 | } 24 | 25 | 26 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Builder/UserDirector.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/DML.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | * Based on James Simones work 4 | * https://github.com/jamessimone/apex-mocks-stress-test/blob/master/src/classes/ICrud.cls 5 | */ 6 | 7 | public virtual class DML implements IDML { 8 | 9 | //By Exploiting compile time polymorphism we can have 'over loaded' methods 10 | // Multiple methods can have the same name with different parameters 11 | //This can also help in bulkifying your APIs 12 | public virtual SObject doInsert(SObject record) { 13 | return this.doInsert(new List{ 14 | record 15 | })[0]; 16 | } 17 | 18 | public virtual List doInsert(List records) { 19 | Database.insert(records); 20 | return records; 21 | } 22 | 23 | public virtual SObject doUpdate(SObject record) { 24 | return this.doUpdate(new List{ 25 | record 26 | })[0]; 27 | } 28 | 29 | public virtual List doUpdate(List records) { 30 | Database.update(records); 31 | return records; 32 | } 33 | 34 | //The Apex Database methods themselves are also overloaded 35 | //The can take a record, a list, a list and boolean etc.. 36 | public virtual SObject doDelete(SObject record) { 37 | return this.doDelete(new List{ 38 | record 39 | })[0]; 40 | } 41 | 42 | public virtual List doDelete(List records) { 43 | return this.doDelete(records, false); 44 | } 45 | 46 | public virtual List doDelete(List records, Boolean allOrNothing) { 47 | Database.delete(records, allOrNothing); 48 | return records; 49 | } 50 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/DML.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/DMLMock.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | public virtual class DMLMock extends DML { 6 | 7 | 8 | public override List doInsert(List records) { 9 | TestingUtils.generateIds(records); 10 | return records; 11 | } 12 | 13 | 14 | public override List doUpdate(List records) { 15 | return records; 16 | } 17 | 18 | 19 | public override List doDelete(List records, Boolean allOrNothing) { 20 | return records; 21 | } 22 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/DMLMock.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/Beverage.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 4/26/2022. 3 | */ 4 | 5 | public abstract class Beverage { 6 | 7 | public String description = 'Beverage'; 8 | 9 | public abstract Double cost(); 10 | 11 | public virtual String getDescription(){ 12 | return this.description; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/Beverage.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/CondimentDecorator.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 4/26/2022. 3 | */ 4 | 5 | public abstract class CondimentDecorator extends Beverage { 6 | 7 | public abstract override String getDescription(); 8 | 9 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/CondimentDecorator.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/Expresso.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 4/26/2022. 3 | */ 4 | 5 | public with sharing class Expresso extends Beverage { 6 | 7 | public Expresso() { 8 | this.description = 'Espresso'; 9 | } 10 | 11 | 12 | public override Double cost() { 13 | return 2.99; 14 | } 15 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/Expresso.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/HouseBlend.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 4/26/2022. 3 | */ 4 | 5 | public with sharing class HouseBlend extends Beverage{ 6 | 7 | public HouseBlend(){ 8 | this.description = 'HouseBlend'; 9 | } 10 | 11 | 12 | public override Double cost() { 13 | System.debug('calling house blend cost'); 14 | return 1.99; 15 | } 16 | 17 | 18 | public override String getDescription() { 19 | return this.description; 20 | } 21 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/HouseBlend.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/Mocha.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 4/26/2022. 3 | */ 4 | 5 | public with sharing class Mocha extends CondimentDecorator { 6 | 7 | Beverage beverage; 8 | 9 | public Mocha(Beverage beverage) { 10 | this.beverage = beverage; 11 | } 12 | 13 | public override String getDescription(){ 14 | return beverage.getDescription() + ' Mocha'; 15 | } 16 | 17 | public override Double cost(){ 18 | System.debug('calling mocha.cost'); 19 | return this.beverage.cost() + .20; 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/Mocha.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/Soy.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 4/26/2022. 3 | */ 4 | 5 | public with sharing class Soy extends CondimentDecorator{ 6 | 7 | Beverage beverage; 8 | 9 | public Soy(Beverage beverage){ 10 | this.beverage = beverage; 11 | } 12 | 13 | 14 | public override String getDescription() { 15 | return this.beverage.description + ' Soy'; 16 | } 17 | 18 | public override Double cost() { 19 | System.debug('calling soy cost'); 20 | return this.beverage.cost() + .50; 21 | } 22 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Decorator/Soy.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/DmlFactory.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | public with sharing class DmlFactory { 6 | 7 | public static Boolean mock = false; 8 | 9 | public static IDML getInstance() { 10 | if (!mock) { 11 | return new DML(); 12 | } else { 13 | return new DMLMock(); 14 | } 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/DmlFactory.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoPizzaStore.cls: -------------------------------------------------------------------------------- 1 | 2 | public with sharing class ChicagoPizzaStore extends PizzaStore { 3 | 4 | /** 5 | * @description lets be fancy and do this one with a reflective factory method. 6 | * We are implementing an abstract method from our super class. It does not know or care 7 | * How we implement create pizza as long we have one. 8 | * @param pizzaType type of pizza to order 9 | * 10 | * @return Pizza 11 | */ 12 | protected override Pizza createPizza(String pizzaType) { 13 | Chicago_Pizza__mdt pizza = Chicago_Pizza__mdt.getInstance(pizzaType); 14 | Type t = Type.forName(pizza.Class_To_Instantiate__c); 15 | return (Pizza) t.newInstance(); 16 | } 17 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoPizzaStore.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoPizzaStore_Tests.cls: -------------------------------------------------------------------------------- 1 | 2 | @IsTest 3 | private class ChicagoPizzaStore_Tests { 4 | @IsTest 5 | static void itShouldReturnCheesePizza() { 6 | //given 7 | PizzaStore store = new ChicagoPizzaStore(); 8 | //when 9 | Test.startTest(); 10 | Pizza pizza = store.orderPizza('cheese'); 11 | Test.stopTest(); 12 | //then 13 | System.assert(pizza instanceof ChicagoStyleCheese); 14 | } 15 | 16 | @IsTest 17 | static void itShouldReturnPepperoniPizza() { 18 | //given 19 | PizzaStore store = new ChicagoPizzaStore(); 20 | //when 21 | Test.startTest(); 22 | Pizza pizza = store.orderPizza('pepperoni'); 23 | Test.stopTest(); 24 | //then 25 | System.assert(pizza instanceof ChicagoStylePepperoni); 26 | } 27 | 28 | @IsTest 29 | static void itShouldReturnSausagePizza() { 30 | //given 31 | PizzaStore store = new ChicagoPizzaStore(); 32 | //when 33 | Test.startTest(); 34 | Pizza pizza = store.orderPizza('sausage'); 35 | Test.stopTest(); 36 | //then 37 | System.assert(pizza instanceof ChicagoStyleSausage); 38 | } 39 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoPizzaStore_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoStyleCheese.cls: -------------------------------------------------------------------------------- 1 | 2 | public with sharing class ChicagoStyleCheese extends Pizza { 3 | 4 | private final String name = 'Chicago style cheese pizza'; 5 | 6 | public override void prepare() { 7 | System.debug('Preparing ' + name); 8 | } 9 | 10 | public override void bake() { 11 | System.debug('Baking ' + name); 12 | } 13 | 14 | public override void cut() { 15 | System.debug('Cutting ' + name); 16 | } 17 | 18 | public override void box() { 19 | System.debug('Boxing ' + name); 20 | } 21 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoStyleCheese.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoStylePepperoni.cls: -------------------------------------------------------------------------------- 1 | 2 | public with sharing class ChicagoStylePepperoni extends Pizza { 3 | 4 | private final String name = 'Chicago style cheese pizza'; 5 | 6 | public override void prepare() { 7 | System.debug('Preparing ' + name); 8 | } 9 | 10 | public override void bake() { 11 | System.debug('Baking ' + name); 12 | } 13 | 14 | public override void cut() { 15 | System.debug('Cutting ' + name); 16 | } 17 | 18 | public override void box() { 19 | System.debug('Boxing ' + name); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoStylePepperoni.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoStyleSausage.cls: -------------------------------------------------------------------------------- 1 | 2 | public with sharing class ChicagoStyleSausage extends Pizza { 3 | 4 | private final String name = 'Chicago style cheese pizza'; 5 | 6 | public override void prepare() { 7 | System.debug('Preparing ' + name); 8 | } 9 | 10 | public override void bake() { 11 | System.debug('Baking ' + name); 12 | } 13 | 14 | public override void cut() { 15 | System.debug('Cutting ' + name); 16 | } 17 | 18 | public override void box() { 19 | System.debug('Boxing ' + name); 20 | } 21 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/ChicagoStyleSausage.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/DependentPizzaStore.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * This is what we do not want 3 | */ 4 | 5 | public with sharing class DependentPizzaStore { 6 | 7 | // New is Glue. This class is dependent on 6 other classes to function. 8 | //We don't really want our Pizza store to have all these dependencies. 9 | // Get our objects from a factory instead. 10 | //If we add more types or styles we will always be modifying the class 11 | public Pizza createPizza(String style, String type){ 12 | Pizza pizza = null; 13 | if (style == 'NY') { 14 | switch on type{ 15 | when 'cheese' { 16 | pizza = new NewYorkStyleCheese(); 17 | } 18 | when 'sausage'{ 19 | pizza = new NewYorkStyleSausage(); 20 | } 21 | when 'pepperoni'{ 22 | pizza = new NewYorkStylePepperoni(); 23 | } 24 | } 25 | } 26 | else if (style == 'Chicago'){ 27 | switch on type{ 28 | when 'cheese' { 29 | pizza = new ChicagoStyleCheese(); 30 | } 31 | when 'sausage'{ 32 | pizza = new ChicagoStyleSausage(); 33 | }when 'pepperoni'{ 34 | pizza = new ChicagoStylePepperoni(); 35 | } 36 | } 37 | } 38 | pizza.prepare(); 39 | pizza.bake(); 40 | pizza.cut(); 41 | pizza.box(); 42 | return pizza; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/DependentPizzaStore.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkPizzaStore.cls: -------------------------------------------------------------------------------- 1 | 2 | public with sharing class NewYorkPizzaStore extends PizzaStore { 3 | 4 | /** 5 | * @description a concrete implementation from our abstract method. 6 | * The order pizza method is defined in our abstact class and will be inherited. 7 | * The order pizza method has no idea what concrete classes are involved. 8 | * 9 | * @param pizzaType name of pizza to order 10 | * 11 | * @return pizza 12 | */ 13 | protected override Pizza createPizza(String pizzaType) { 14 | Pizza pizza; 15 | switch on pizzaType.toLowerCase() { 16 | when 'cheese' { 17 | pizza = new NewYorkStyleCheese(); 18 | } 19 | when 'sausage' { 20 | pizza = new NewYorkStyleSausage(); 21 | }when 'pepperoni' { 22 | pizza = new NewYorkStylePepperoni(); 23 | } 24 | } 25 | return pizza; 26 | } 27 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkPizzaStore.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkPizzaStore_Tests.cls: -------------------------------------------------------------------------------- 1 | 2 | @IsTest 3 | private class NewYorkPizzaStore_Tests { 4 | @IsTest 5 | static void itShouldReturnCheesePizza() { 6 | //given 7 | PizzaStore store = new NewYorkPizzaStore(); 8 | //when 9 | Test.startTest(); 10 | Pizza pizza = store.orderPizza('cheese'); 11 | Test.stopTest(); 12 | //then 13 | System.assert(pizza instanceof NewYorkStyleCheese); 14 | } 15 | 16 | @IsTest 17 | static void itShouldReturnSausagePizza() { 18 | //given 19 | PizzaStore store = new NewYorkPizzaStore(); 20 | //when 21 | Test.startTest(); 22 | Pizza pizza = store.orderPizza('sausage'); 23 | Test.stopTest(); 24 | //then 25 | System.assert(pizza instanceof NewYorkStyleSausage); 26 | } 27 | 28 | @IsTest 29 | static void itShouldReturnPepperoniPizza() { 30 | //given 31 | PizzaStore store = new NewYorkPizzaStore(); 32 | //when 33 | Test.startTest(); 34 | Pizza pizza = store.orderPizza('pepperoni'); 35 | Test.stopTest(); 36 | //then 37 | System.assert(pizza instanceof NewYorkStylePepperoni); 38 | } 39 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkPizzaStore_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkStyleCheese.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks on 9/18/2021. 3 | */ 4 | 5 | public with sharing class NewYorkStyleCheese extends Pizza { 6 | private final String name = 'New York style cheese pizza'; 7 | 8 | public override void prepare() { 9 | System.debug('Preparing ' + name); 10 | } 11 | 12 | public override void bake() { 13 | System.debug('Baking ' + name); 14 | } 15 | 16 | public override void cut() { 17 | System.debug('Cutting ' + name); 18 | } 19 | 20 | public override void box() { 21 | System.debug('Boxing ' + name); 22 | } 23 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkStyleCheese.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkStylePepperoni.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks on 9/18/2021. 3 | */ 4 | 5 | public with sharing class NewYorkStylePepperoni extends Pizza { 6 | 7 | private final String name = 'New York style pepperoni pizza'; 8 | 9 | public override void prepare() { 10 | System.debug('Preparing ' + name); 11 | } 12 | 13 | public override void bake() { 14 | System.debug('Baking ' + name); 15 | } 16 | 17 | public override void cut() { 18 | System.debug('Cutting ' + name); 19 | } 20 | 21 | public override void box() { 22 | System.debug('Boxing ' + name); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkStylePepperoni.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkStyleSausage.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks on 9/18/2021. 3 | */ 4 | 5 | public with sharing class NewYorkStyleSausage extends Pizza { 6 | 7 | private final String name = 'New York style sausage pizza'; 8 | 9 | public override void prepare() { 10 | System.debug('Preparing ' + name); 11 | } 12 | 13 | public override void bake() { 14 | System.debug('Baking ' + name); 15 | } 16 | 17 | public override void cut() { 18 | System.debug('Cutting ' + name); 19 | } 20 | 21 | public override void box() { 22 | System.debug('Boxing ' + name); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/NewYorkStyleSausage.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/PizzaStore.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Unlike the simple factory we are will be using a super class for our pizza store 3 | * and moving the create pizza method into it. 4 | */ 5 | public with sharing abstract class PizzaStore { 6 | 7 | /** 8 | * @description All concrete classes inherit this method 9 | * @param pizzaType 10 | * 11 | * @return 12 | */ 13 | public Pizza orderPizza(String pizzaType) { 14 | Pizza pizza; 15 | 16 | pizza = createPizza(pizzaType); 17 | pizza.prepare(); 18 | pizza.bake(); 19 | pizza.cut(); 20 | pizza.box(); 21 | 22 | return pizza; 23 | } 24 | 25 | /** 26 | * @Description the abstract method means that all concrete classes 27 | * must implement a version of this method. It will be our new factory. 28 | * @param pizzaType 29 | * 30 | * @return 31 | */ 32 | protected abstract Pizza createPizza(String pizzaType); 33 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/FactoryMethod/PizzaStore.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/IDML.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | * This is a stripped down verion of James Simones 4 | */ 5 | 6 | public interface IDML { 7 | SObject doInsert(SObject record); 8 | List doInsert(List records); 9 | SObject doUpdate(SObject record); 10 | List doUpdate(List records); 11 | SObject doDelete(SObject record); 12 | List doDelete(List records); 13 | List doDelete(List records, Boolean allOrNothing); 14 | 15 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/IDML.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/ISelector.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/26/2022. 3 | */ 4 | 5 | public interface ISelector { 6 | 7 | List query(String queryString); 8 | 9 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/ISelector.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/CardFactory.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | public with sharing class CardFactory { 6 | 7 | public static CreditCard getInstance(PaymentController2.PaymentDetails details) { 8 | switch on details.cardType.toLowerCase() { 9 | when 'visa' { 10 | return new Visa(details); 11 | } when 'mastercard' { 12 | return new MasterCard(details); 13 | } when 'discover' { 14 | return new Discover(details); 15 | } 16 | } 17 | return null; 18 | } 19 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/CardFactory.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/CardFactory_Tests.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 6/11/2022. 3 | */ 4 | 5 | @IsTest 6 | private class CardFactory_Tests { 7 | @IsTest 8 | static void ItShouldReturnVisa() { 9 | //given 10 | CreditCard card = null; 11 | PaymentController2.PaymentDetails details = new PaymentController2.PaymentDetails('fake@fake.net', 'Visa', '3/3/2020', 12 | '1234', '1234', 10.0); 13 | //when 14 | card = CardFactory.getInstance(details); 15 | //then 16 | System.assert(card instanceof Visa, 'It should return the correct instance'); 17 | } 18 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/CardFactory_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/CardType.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | public enum CardType { 6 | VISA, MASTERCARD, DISCOVER 7 | 8 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/CardType.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/CreditCard.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | //Abstract Credit Card 6 | //Provides all the details that each subclass needs 7 | //leave implementation of verify card and process payment to child classes 8 | //We can also achieve polymorphism with interfaces and virtual classes 9 | public with sharing abstract class CreditCard { 10 | //Used to throw exceptions if a process fails 11 | public class CreditCardException extends Exception { 12 | } 13 | 14 | //protected gives access to all subclasses 15 | //We use composition to model as 'has a' relationship 16 | protected PaymentController2.PaymentDetails details; 17 | 18 | public CreditCard(PaymentController2.PaymentDetails details) { 19 | this.details = details; 20 | } 21 | 22 | //Class that extends CreditCard must implement these 23 | protected abstract void verifyCard(); 24 | protected abstract void processPayment(); 25 | 26 | //leaving it a empty virual method can allow it to be optional 27 | protected virtual void validateAddress() { 28 | } 29 | 30 | //Example of a template pattern. Leave methods that concrete credit cards must implement. 31 | public void submitPayment() { 32 | verifyCard(); 33 | validateAddress(); 34 | processPayment(); 35 | } 36 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/CreditCard.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/Discover.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | public with sharing class Discover extends CreditCard { 6 | 7 | public Discover(PaymentController2.PaymentDetails details) { 8 | super(details); 9 | } 10 | 11 | 12 | protected override void verifyCard() { 13 | System.debug('Verifying card ' + this.details.cardType); 14 | if (this.details.cardNumber.length() != 12) { 15 | throw new CreditCardException('Discover card must have a 10 digit number'); 16 | } 17 | } 18 | 19 | protected override void processPayment() { 20 | System.debug('processing payment for Discover ' + this.details.amount); 21 | } 22 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/Discover.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/MasterCard.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | public with sharing class MasterCard extends CreditCard { 6 | 7 | public MasterCard(PaymentController2.PaymentDetails details) { 8 | super(details); 9 | } 10 | 11 | protected override void validateAddress() { 12 | System.debug('The mastercard is validating the address of the user '); 13 | } 14 | 15 | 16 | protected override void verifyCard() { 17 | System.debug('Verifying card ' + this.details.cardType); 18 | if (this.details.cardNumber.length() != 11) { 19 | throw new CreditCardException('Master Card must have a 11 digit number'); 20 | } 21 | } 22 | 23 | protected override void processPayment() { 24 | System.debug('processing master card payment for ' + this.details.amount); 25 | } 26 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/MasterCard.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/PaymentController1.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | public with sharing class PaymentController1 { 6 | 7 | public static void submitPayment(String emailAddress, String cardType, String cardNumber, String cardExpiration, String securityCode, Decimal amount) { 8 | 9 | //verify card 10 | if (cardType == 'Visa') { 11 | if (cardNumber.length() != 10) { 12 | System.debug('failed visa validation'); 13 | } else if (cardType == 'MasterCard') { 14 | if (cardNumber.length() != 11) { 15 | System.debug('failed master card validation'); 16 | } 17 | } else if (cardType == 'Discover') { 18 | if (cardNumber.length() != 12) { 19 | System.debug('failed discover card validation'); 20 | } 21 | } 22 | } 23 | //process the payment 24 | if (cardType == 'Visa') { 25 | System.debug('call the visa payment system'); 26 | HttpRequest request = new HttpRequest(); 27 | request.setEndpoint('www.fakevisa.com'); 28 | Http http = new Http(); 29 | HttpResponse response = http.send(request); 30 | if (response.getStatusCode() == 200) { 31 | // Insert new successful record 32 | } 33 | } else if (cardType == 'MasterCard') { 34 | System.debug('call the visa payment system'); 35 | HttpRequest request = new HttpRequest(); 36 | request.setEndpoint('www.fakeMC.com'); 37 | Http http = new Http(); 38 | HttpResponse response = http.send(request); 39 | if (response.getStatusCode() == 200) { 40 | // Insert new successful record 41 | } 42 | 43 | } else if (cardType == 'Discover') { 44 | System.debug('call the visa payment system'); 45 | HttpRequest request = new HttpRequest(); 46 | request.setEndpoint('www.fakeDiscover.com'); 47 | Http http = new Http(); 48 | HttpResponse response = http.send(request); 49 | if (response.getStatusCode() == 200) { 50 | // Insert new successful record 51 | } 52 | } 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/PaymentController1.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/PaymentController2.cls: -------------------------------------------------------------------------------- 1 | 2 | //1. Remove the long parameter list and place it in its own object 3 | //2. Move the logic into the class where it belongs. A credit card -avoids primitive obsession. Use object to express concepts 4 | //3. The service class will use Composition and polymorphism to determine the correct algorithm at run time 5 | public with sharing class PaymentController2 { 6 | 7 | //change to receive a JSON string 8 | //Keep our controller as thing and logic-less and possible 9 | //TODO bulkify and overload these methods 10 | //Presentation layer 11 | public static void submitPayment(String paymentDetails) { 12 | try { 13 | submitPayment((PaymentDetails) JSON.deserialize(paymentDetails, PaymentDetails.class)); 14 | } catch (Exception e) { 15 | throw new AuraHandledException('There was a problem processing the order'); 16 | } 17 | } 18 | 19 | public static void submitPayment(PaymentDetails paymentDetails) { 20 | //Over load this method and forward the payment processing to the service layer 21 | PaymentService paymentService = new PaymentService(paymentDetails); 22 | paymentService.processOrder(); 23 | } 24 | 25 | /** 26 | * A wrapper class for our PaymentDetails this does not have to be an inner class. 27 | */ 28 | public class PaymentDetails { 29 | public String emailAddress { get; private set; } 30 | public String cardType { get; private set; } 31 | public String cardNumber { get; private set; } 32 | public String cardExpiration { get; private set; } 33 | public String securityCode { get; private set; } 34 | public Decimal amount { get; private set; } 35 | 36 | 37 | public PaymentDetails(String emailAddress, String cardType, String cardNumber, String cardExpiration, 38 | String securityCode, Decimal amount) { 39 | this.emailAddress = emailAddress; 40 | this.cardNumber = cardNumber; 41 | this.cardType = cardType; 42 | this.cardExpiration = cardExpiration; 43 | this.securityCode = securityCode; 44 | this.amount = amount; 45 | } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/PaymentController2.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/PaymentController2_Tests.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | @IsTest 6 | private class PaymentController2_Tests { 7 | @IsTest 8 | static void itShouldthrowException() { 9 | //given 10 | PaymentController2.PaymentDetails details = new PaymentController2.PaymentDetails('brooks@fake.net', 'Visa', '123456789', 11 | '01/2020', 'abc123', 19.99); 12 | String detailsString = JSON.serialize(details); 13 | try {//when 14 | PaymentController2.submitPayment(detailsString); 15 | } catch (Exception e) {//then 16 | System.assert(e instanceof AuraHandledException); 17 | } 18 | } 19 | 20 | @isTest 21 | static void itShouldProcessTheOrder() { 22 | //given 23 | PaymentController2.PaymentDetails details = new PaymentController2.PaymentDetails('brooks@fake.net', 'Visa', '1234567890', 24 | '01/2020', 'abc123', 19.99); 25 | String detailsString = JSON.serialize(details); 26 | Exception ex = null; 27 | //when 28 | try { 29 | PaymentController2.submitPayment(detailsString); 30 | } catch (Exception e) { 31 | System.debug('We should not have thrown an exception'); 32 | } 33 | System.assertEquals(ex, null, 'No exception should have been thrown'); 34 | } 35 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/PaymentController2_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/PaymentService.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | //The Payment Service class is using composition, DI and polymorphism to get the correct CreditCard object at run time. 6 | //The details of how each credit card implements its verification and payment method belong to the concrete credit cards 7 | public with sharing class PaymentService { 8 | //Objects built of objects = composition 9 | //Because we uses an abstract type CreditCard we can assign any object to it that extends that type 10 | //We can add more cards classes as needed 11 | 12 | //Use of composition a payment service has a credit card... 13 | //This is different than inheritance. A visa card is a credit card 14 | CreditCard card; 15 | 16 | public PaymentService(PaymentController2.PaymentDetails paymentDetails) { 17 | //We use a simple factory method to inject the concrete credit card object at run time 18 | //Each card can implement its own algorithms - simple implementation of the Strategy Pattern 19 | this.card = CardFactory.getInstance(paymentDetails); 20 | } 21 | 22 | //delegate verify card and process order to the Credit card class 23 | public void processOrder() { 24 | this.card.submitPayment(); 25 | } 26 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/PaymentService.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/Visa.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 4/30/2022. 3 | */ 4 | 5 | public with sharing class Visa extends CreditCard { 6 | 7 | public Visa(PaymentController2.PaymentDetails details) { 8 | super(details); 9 | } 10 | 11 | protected override void verifyCard() { 12 | System.debug('Verifying card ' + this.details.cardType); 13 | if (this.details.cardNumber.length() != 10) { 14 | throw new CreditCardException('Visa card must have a 10 digit number'); 15 | } 16 | } 17 | 18 | protected override void processPayment() { 19 | System.debug('processing payment for ' + this.details.amount); 20 | //if something went wrong with the payment. It was rejected we can throw a credit card exception. 21 | } 22 | 23 | 24 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/OO_Fundamentals/Visa.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/IObserver.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/26/2022. 3 | */ 4 | 5 | public interface IObserver { 6 | void notify(); 7 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/IObserver.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ISubject.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/12/2022. 3 | */ 4 | 5 | public interface ISubject { 6 | 7 | void subscribe(Product_Notification__c observer); 8 | void unsubscribe(Product_Notification__c observer); 9 | void notifyObservers(); 10 | 11 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ISubject.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ObserverPatternController.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/12/2022. 3 | */ 4 | 5 | public with sharing class ObserverPatternController { 6 | public static final String PRODUCT_QUERY = 7 | 'SELECT Id, Name, Is_Available__c, Product_Name__c ' + 8 | 'FROM Product_Catalog__c ' + 9 | 'WITH SECURITY_ENFORCED LIMIT 1'; 10 | 11 | @AuraEnabled(Cacheable=true) 12 | public static List getProducts() { 13 | ISelector selector = SelectorFactory.getSelector(); 14 | return (List) selector.query(PRODUCT_QUERY); 15 | } 16 | 17 | @AuraEnabled 18 | public static void subscribeUser(String productId, String email) { 19 | Product_Notification__c observer = new Product_Notification__c(Email_Address__c = email); 20 | ISubject productSubject = new ProductSubject(productId); 21 | productSubject.subscribe(observer); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ObserverPatternController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ProductCatalogTriggerHandler.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/27/2022. 3 | */ 4 | 5 | public with sharing class ProductCatalogTriggerHandler extends TriggerHandler { 6 | 7 | protected override void beforeUpdate() { 8 | for (Product_Catalog__c catalog : (List) Trigger.new) { 9 | Product_Catalog__c oldCatalog = (Product_Catalog__c) Trigger.oldMap.get(catalog.Id); 10 | if (catalog.Is_Available__c == true && oldCatalog.Is_Available__c == false) { 11 | ProductSubject subject = new ProductSubject(catalog.Id); 12 | subject.notifyObservers(); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ProductCatalogTriggerHandler.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ProductObserver.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/26/2022. 3 | */ 4 | 5 | public with sharing class ProductObserver implements IObserver { 6 | String id; 7 | 8 | public ProductObserver(String id) { 9 | this.id = id; 10 | } 11 | 12 | public void notify() { 13 | List observers = [ 14 | SELECT Id, Email_Address__c 15 | FROM Product_Notification__c 16 | WHERE Product_Catelog__c = :this.id 17 | AND Subscribe__c = TRUE 18 | WITH SECURITY_ENFORCED 19 | ]; 20 | 21 | //mock email 22 | for (Product_Notification__c observer : observers) { 23 | System.debug('sending email to' + observer.Email_Address__c); 24 | } 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ProductObserver.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ProductSubject.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * The publisher in the pub sub relationship 3 | */ 4 | public with sharing class ProductSubject implements ISubject { 5 | 6 | String id; 7 | 8 | public ProductSubject(String id) { 9 | this.id = id; 10 | } 11 | 12 | public void subscribe(Product_Notification__c observer) { 13 | observer.Product_Catelog__c = this.id; 14 | observer.Subscribe__c = true; 15 | 16 | insert observer; 17 | } 18 | 19 | public void unsubscribe(Product_Notification__c observer) { 20 | System.debug('Doing unsubscribe'); 21 | } 22 | 23 | public void notifyObservers() { 24 | IObserver observer = new ProductObserver(this.id); 25 | observer.notify(); 26 | } 27 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Observer/ProductSubject.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/Athlete.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 11/3/2022. 3 | */ 4 | 5 | public abstract class Athlete { 6 | 7 | protected String name; 8 | 9 | public Athlete(String name) { 10 | this.name = name; 11 | } 12 | 13 | public class GoldWinner extends Athlete { 14 | private Athlete.GoldWinner goldWinner; 15 | 16 | private GoldWinner(String name) { 17 | super(name); 18 | } 19 | 20 | 21 | } 22 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/Athlete.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/Customer2.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 11/3/2022. 3 | */ 4 | 5 | public abstract class Customer2 { 6 | 7 | public String customerRating {get; private set; } 8 | public static final Integer PREMIER = 2; 9 | public static final Integer VALUED = 1; 10 | public static final Integer DEADBEAT = 0; 11 | 12 | public String getCustomerRating(){ 13 | return customerRating; 14 | } 15 | 16 | public class Premier extends Customer2 { 17 | 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/Customer2.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/composing_methods/ExtractMethodSample.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 5/6/2022. 3 | */ 4 | 5 | public with sharing class ExtractMethodSample { 6 | private List integers; 7 | private String name; 8 | 9 | private Integer numberOfLateDeliveries; 10 | 11 | 12 | public extractMethodSample(List integers, String name) { 13 | this.integers = integers; 14 | this.name = name; 15 | } 16 | public void printOwing(){ 17 | Decimal outstanding = getOutstanding(); 18 | printBanner(); 19 | printDetails(outstanding); 20 | 21 | } 22 | 23 | private Decimal getOutstanding() { 24 | Decimal result = 0.0; 25 | for(Integer i : integers){ 26 | result += i; 27 | } 28 | return result; 29 | } 30 | 31 | private void printDetails(Decimal outstanding) { 32 | System.debug('Name: ' + name); 33 | System.debug('amount: ' + outstanding); 34 | } 35 | 36 | private void printBanner() { 37 | System.debug('***********************************************'); 38 | System.debug('******************* Customer Owes *************'); 39 | System.debug('************************************************'); 40 | } 41 | 42 | //inline a method 43 | public Integer getrating(){ 44 | return numberOfLateDeliveries > 5 ? 2 : 1; 45 | } 46 | //inlined above 47 | public boolean moreThan5LateDeliveries(){ 48 | return numberOfLateDeliveries > 5; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/composing_methods/ExtractMethodSample.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/composing_methods/ReplaceTempWithQuerySample.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 5/6/2022. 3 | */ 4 | 5 | public with sharing class ReplaceTempWithQuerySample { 6 | 7 | /*private Integer quantity; 8 | private Integer itemPrice; 9 | 10 | //Method with temp variables 11 | public Decimal getPrice1(){ 12 | Integer basePrice = this.quantity * this.itemPrice; 13 | Double discountFactor; 14 | basePrice > 1000 ? discountFactor = .95 : discountFactor = .98; 15 | return basePrice * discountFactor; 16 | } 17 | 18 | //refactored method using temp to query 19 | public Decimal getPrice(){ 20 | return getBasePrice() * getDiscountFactor(); 21 | } 22 | 23 | private Decimal getDiscountFactor() { 24 | return getBasePrice() > 100 ? .95 : .98; 25 | } 26 | 27 | private Integer getBasePrice() { 28 | return quantity * itemPrice; 29 | }*/ 30 | 31 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/composing_methods/ReplaceTempWithQuerySample.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/replaceConditionalWithStrategy/CalculateSalary1.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 12/20/2022. 3 | */ 4 | 5 | public with sharing class CalculateSalary1 { 6 | 7 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/replaceConditionalWithStrategy/CalculateSalary1.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/replaceConditionalWithStrategy/Employee1.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 12/20/2022. 3 | */ 4 | 5 | public with sharing class Employee1 { 6 | 7 | private Boolean bonus = false; 8 | private Double salary = 0.0; 9 | private Double bonusAmount = 0.15; 10 | 11 | public Employee1(Double salary, Boolean bonus, Double bonusAmount) { 12 | this.salary = salary; 13 | this.bonus = bonus; 14 | this.bonusAmount = bonusAmount; 15 | } 16 | 17 | public Employee1(Double salary, Boolean bonus){ 18 | this.salary = salary; 19 | this.bonus = bonus; 20 | } 21 | 22 | public Employee1(){} 23 | 24 | public Double getSalary() { 25 | return salary + (salary * bonusAmount); 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/replaceConditionalWithStrategy/Employee1.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/replaceConditionalWithStrategy/aura/Pay/Pay.intf: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/replaceConditionalWithStrategy/aura/Pay/Pay.intf-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Pay 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/simplify_ conditionals/ReplaceConditionalWithGuardClause.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/13/2022. 3 | */ 4 | 5 | /** 6 | * We use this when a method has conditional behavior that does not make clear the normal path of execution 7 | */ 8 | public with sharing class ReplaceConditionalWithGuardClause { 9 | Boolean isDead; 10 | Boolean isSeperated; 11 | Boolean isRetired; 12 | 13 | /** 14 | * The normal behavior of the method to return the normal pay amount is obscured in 15 | * the conditional logic. 16 | * @return calculate pay amount 17 | */ 18 | public Double getPayAmount() { 19 | if (isDead) { 20 | return deadAmount(); 21 | } 22 | if (isRetired) { 23 | return retiredAmount(); 24 | } 25 | if (isSeperated) { 26 | return seperatedAmount(); 27 | } 28 | return normalPayAmount(); 29 | } 30 | 31 | 32 | private Double normalPayAmount() { 33 | return null; 34 | } 35 | 36 | private Double deadAmount(){ 37 | return null; 38 | } 39 | 40 | private Double seperatedAmount(){ 41 | return null; 42 | } 43 | 44 | private Double retiredAmount(){ 45 | return null; 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Refactoring/simplify_ conditionals/ReplaceConditionalWithGuardClause.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/ReflectiveFactory/IPizzaFactory.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/24/2022. 3 | */ 4 | 5 | public interface IPizzaFactory { 6 | 7 | Pizza createPizza(String type); 8 | 9 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/ReflectiveFactory/IPizzaFactory.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/ReflectiveFactory/ReflectivePizzaFactory.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Instead of updating our factory every time we need to add a concrete pizza we simple add a value to custom metadata 3 | * and let the Type class instantiate it. 4 | * Note this only works with a no argument constructor. 5 | */ 6 | 7 | /*In object-oriented programming languages, reflection allows inspection of classes, interfaces, fields and methods 8 | at runtime without knowing the names of the interfaces, fields, methods at compile time. 9 | It also allows instantiation of new objects and invocation of methods.*/ 10 | 11 | public with sharing class ReflectivePizzaFactory implements IPizzaFactory{ 12 | //test 13 | public Pizza createPizza(String typeOfPizza){ 14 | Pizza__mdt pizza = Pizza__mdt.getInstance(typeOfPizza); 15 | Type t = Type.forName(pizza.Class_To_Instantiate__c); 16 | return (Pizza)t.newInstance(); 17 | } 18 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/ReflectiveFactory/ReflectivePizzaFactory.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/ReflectiveFactory/ReflectivePizzaFactory_Tests.cls: -------------------------------------------------------------------------------- 1 | 2 | @IsTest 3 | private class ReflectivePizzaFactory_Tests { 4 | @IsTest 5 | static void itShouldReturnCheesePizza() { 6 | 7 | ReflectivePizzaFactory factory = new ReflectivePizzaFactory(); 8 | Pizza pizza; 9 | //when 10 | Test.startTest(); 11 | pizza = factory.createPizza('cheese'); 12 | Test.stopTest(); 13 | //then 14 | System.assert(pizza instanceof CheesePizza); 15 | } 16 | 17 | @IsTest 18 | static void itShouldReturnTacoPizza(){ 19 | //given 20 | ReflectivePizzaFactory factory = new ReflectivePizzaFactory(); 21 | Pizza pizza; 22 | //when 23 | Test.startTest(); 24 | pizza = factory.createPizza('taco'); 25 | Test.stopTest(); 26 | //then 27 | System.assert(pizza instanceof TacoPizza); 28 | } 29 | 30 | @IsTest 31 | static void itShouldReturnPepperoniPizza(){ 32 | //given 33 | ReflectivePizzaFactory factory = new ReflectivePizzaFactory(); 34 | Pizza pizza; 35 | //when 36 | Test.startTest(); 37 | pizza = factory.createPizza('pepperoni'); 38 | Test.stopTest(); 39 | //then 40 | System.assert(pizza instanceof PepperoniPizza); 41 | } 42 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/ReflectiveFactory/ReflectivePizzaFactory_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Selector.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/26/2022. 3 | */ 4 | 5 | public with sharing class Selector implements ISelector { 6 | 7 | private String queryString; 8 | 9 | public List query(String queryString) { 10 | return Database.query(queryString); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Selector.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SelectorFacory_Tests.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/26/2022. 3 | */ 4 | 5 | @IsTest 6 | private class SelectorFacory_Tests { 7 | @IsTest 8 | static void returnStandardSelector() { 9 | //given 10 | ISelector selector; 11 | //when 12 | selector = SelectorFactory.getSelector(); 13 | //then 14 | System.assert(selector instanceof Selector, 'It should return the actual selector'); 15 | } 16 | 17 | @IsTest 18 | static void returnMockSelector() { 19 | //given 20 | SelectorFactory.withMocks = true; 21 | ISelector selector; 22 | //when 23 | selector = SelectorFactory.getSelector(); 24 | //then 25 | System.assert(selector instanceof SelectorMock, 'It should return the mock selector'); 26 | } 27 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SelectorFacory_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SelectorFactory.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/26/2022. 3 | */ 4 | 5 | public with sharing class SelectorFactory { 6 | public static Boolean withMocks = false; 7 | 8 | public static ISelector getSelector() { 9 | if (!withMocks) { 10 | return new Selector(); 11 | } else { 12 | return new SelectorMock(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SelectorFactory.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SelectorMock.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/26/2022. 3 | */ 4 | 5 | public with sharing class SelectorMock implements ISelector { 6 | 7 | public List data; 8 | 9 | 10 | public List query(String queryString) { 11 | return this.data; 12 | } 13 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SelectorMock.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/CheesePizza.cls: -------------------------------------------------------------------------------- 1 | 2 | public with sharing class CheesePizza extends Pizza { 3 | public final String name = 'Cheese Pizza'; 4 | 5 | public override void prepare() { 6 | System.debug('Preparing a ' + this.name); 7 | } 8 | 9 | public override void bake() { 10 | System.debug('baking ' + this.name); 11 | } 12 | 13 | public override void cut() { 14 | System.debug('cutting ' + this.name); 15 | } 16 | 17 | public override void box() { 18 | System.debug('boxing ' + this.name); 19 | } 20 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/CheesePizza.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/PepperoniPizza.cls: -------------------------------------------------------------------------------- 1 | 2 | public with sharing class PepperoniPizza extends Pizza{ 3 | 4 | private final String name = 'Pepperoni Pizza'; 5 | 6 | public override void prepare() { 7 | System.debug('Preparing a ' + this.name); 8 | } 9 | 10 | public override void bake() { 11 | System.debug('baking ' + this.name); 12 | } 13 | 14 | public override void cut() { 15 | System.debug('cutting ' + this.name); 16 | } 17 | 18 | public override void box() { 19 | System.debug('boxing ' + this.name); 20 | } 21 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/PepperoniPizza.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/Pizza.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * @description this is the super class that all our Pizzas will inherit from. 3 | * It is important to understand that when someone says program to an interface and not 4 | * an implementation they really mean program to a super type. Not necessarily to the Interface data structure. 5 | * In Apex this could be a virtual class and abstract class or an interface 6 | */ 7 | 8 | public with sharing abstract class Pizza { 9 | 10 | //All our concrete pizzas will need to implement these methods 11 | 12 | public abstract void prepare(); 13 | public abstract void bake(); 14 | public abstract void cut(); 15 | public abstract void box(); 16 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/Pizza.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/SimplePizzaFactory.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * @description From Head First Design Patterns. The authors feel that this not technically a design pattern. 3 | * Because it is not part of the GOF book. But this type of simple implementation often serves me well. 4 | * 5 | * The flexibility here comes from the super class. We can have Pizza on the left hand side of the = 6 | * But a concrete pizza class on the left 7 | */ 8 | 9 | public with sharing class SimplePizzaFactory implements IPizzaFactory{ 10 | 11 | public Pizza createPizza(String typeOfPizza) { 12 | Pizza pizza = null; 13 | switch on typeOfPizza.toLowerCase() { 14 | when 'cheese' { 15 | pizza = new CheesePizza(); 16 | } 17 | when 'taco' { 18 | pizza = new TacoPizza(); 19 | }when 'pepperoni' { 20 | pizza = new PepperoniPizza(); 21 | } 22 | } 23 | return pizza; 24 | } 25 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/SimplePizzaFactory.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/SimplePizzaFactory_Tests.cls: -------------------------------------------------------------------------------- 1 | 2 | @IsTest 3 | private class SimplePizzaFactory_Tests { 4 | @IsTest 5 | static void itShouldReturnCheesePizza() { 6 | //given 7 | SimplePizzaFactory factory = new SimplePizzaFactory(); 8 | Pizza pizza; 9 | //when 10 | Test.startTest(); 11 | pizza = factory.createPizza('cheese'); 12 | Test.stopTest(); 13 | //then 14 | System.assert(pizza instanceof CheesePizza); 15 | } 16 | 17 | @IsTest 18 | static void itShouldReturnTacoPizza(){ 19 | //given 20 | SimplePizzaFactory factory = new SimplePizzaFactory(); 21 | Pizza pizza; 22 | //when 23 | Test.startTest(); 24 | pizza = factory.createPizza('taco'); 25 | Test.stopTest(); 26 | //then 27 | System.assert(pizza instanceof TacoPizza); 28 | } 29 | 30 | @IsTest 31 | static void itShouldReturnPepperoniPizza(){ 32 | //given 33 | SimplePizzaFactory factory = new SimplePizzaFactory(); 34 | Pizza pizza; 35 | //when 36 | Test.startTest(); 37 | pizza = factory.createPizza('pepperoni'); 38 | Test.stopTest(); 39 | //then 40 | System.assert(pizza instanceof PepperoniPizza); 41 | } 42 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/SimplePizzaFactory_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/SimplePizzaStore.cls: -------------------------------------------------------------------------------- 1 | 2 | public with sharing class SimplePizzaStore { 3 | 4 | IPizzaFactory factory; 5 | 6 | /** 7 | * @description We can pass the appropriate factory in to the constructor at run time 8 | * @param factory The Factory class we want to use at run time 9 | */ 10 | public SimplePizzaStore(IPizzaFactory factory){ 11 | this.factory = factory; 12 | } 13 | /** 14 | * @param type The type of pizza we want from the factory 15 | * 16 | * @return a concrete pizza 17 | */ 18 | public Pizza orderPizza(String type){ 19 | Pizza pizza; 20 | pizza = factory.createPizza(type); 21 | //Because we are using the Pizza superclass as the type. We know that any concrete pizza 22 | //has these methods. 23 | pizza.prepare(); 24 | pizza.bake(); 25 | pizza.cut(); 26 | pizza.box(); 27 | 28 | return pizza; 29 | } 30 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/SimplePizzaStore.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/SimplePizzaStore_Tests.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks on 9/18/2021. 3 | */ 4 | 5 | @IsTest 6 | private class SimplePizzaStore_Tests { 7 | @IsTest 8 | static void itShouldMakeAPizza() { 9 | //given 10 | SimplePizzaStore store = new SimplePizzaStore(new ReflectivePizzaFactory()); 11 | //when 12 | Test.startTest(); 13 | Pizza pizza = store.orderPizza('cheese'); 14 | Test.stopTest(); 15 | //then 16 | System.assert(pizza instanceof CheesePizza, 'It should make a cheese pizza'); 17 | } 18 | @IsTest 19 | static void isShouldMakeAPizzaWithSimpleFactory(){ 20 | //given 21 | SimplePizzaStore store = new SimplePizzaStore(new SimplePizzaFactory()); 22 | //when 23 | Pizza pizza = store.orderPizza('pepperoni'); 24 | //then 25 | System.assert(pizza instanceof PepperoniPizza); 26 | } 27 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/SimplePizzaStore_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/TacoPizza.cls: -------------------------------------------------------------------------------- 1 | 2 | public with sharing class TacoPizza extends Pizza { 3 | private final String name = 'Taco Pizza'; 4 | 5 | public override void prepare(){ 6 | System.debug('Preparing a ' + this.name); 7 | } 8 | 9 | public override void bake(){ 10 | System.debug('Baking a ' + this.name); 11 | } 12 | 13 | public override void cut(){ 14 | System.debug('Cutting a ' + this.name); 15 | } 16 | 17 | public override void box(){ 18 | System.debug('Boxing a ' + this.name); 19 | } 20 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/SimpleFactory/TacoPizza.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/State/CaseHelper.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/15/2022. 3 | * @description This is version of the code prior to the implementation 4 | * of the state pattern 5 | * 6 | * The key is that the behavior of our program is dependent on the state of the Case 7 | */ 8 | 9 | public with sharing class CaseHelper { 10 | 11 | public void closeCase(Case customerCase){ 12 | if (customerCase.Status == 'New') { 13 | //throw error 14 | } else if (customerCase.Status == 'Open'){ 15 | //validate agent entered closed reason 16 | }else if (customerCase.Status == 'In Process'){ 17 | //validate email sent to customer 18 | }else if (customerCase.Status == 'Closed'){ 19 | //throw error case is already closed 20 | }else { 21 | //send email to case owner owner 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/State/CaseHelper.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/State/CloseCaseStatehandler.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/15/2022. 3 | */ 4 | 5 | public with sharing class CloseCaseStatehandler implements ICaseState{ 6 | 7 | 8 | public void close(Case customerCase) { 9 | System.debug('Error case is already closed'); 10 | } 11 | 12 | public void calculateTimeSpent(Case customerCase) { 13 | System.debug('Calculating time spent for closed case '); 14 | } 15 | 16 | public void validate(Case customerCase) { 17 | System.debug('Error closed case can not be validated'); 18 | } 19 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/State/CloseCaseStatehandler.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/State/ICaseState.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/15/2022. 3 | */ 4 | 5 | public interface ICaseState { 6 | 7 | void close(Case customerCase); 8 | void calculateTimeSpent(Case customerCase); 9 | void validate(Case customerCase); 10 | 11 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/State/ICaseState.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/State/InProcessCaseStateHandler.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/15/2022. 3 | */ 4 | 5 | public with sharing class InProcessCaseStateHandler implements ICaseState{ 6 | 7 | 8 | public void close(Case customerCase) { 9 | System.debug(' close in process case'); 10 | } 11 | 12 | public void calculateTimeSpent(Case customerCase) { 13 | System.debug('Calculating time spent in process case from time it was sent to open'); 14 | } 15 | 16 | public void validate(Case customerCase) { 17 | System.debug('Validation in process case'); 18 | } 19 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/State/InProcessCaseStateHandler.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/State/NewCaseState.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/15/2022. 3 | */ 4 | 5 | public with sharing class NewCaseState implements ICaseState{ 6 | 7 | 8 | public void close(Case customerCase) { 9 | System.debug('Closing a new clase'); 10 | } 11 | 12 | public void calculateTimeSpent(Case customerCase) { 13 | System.debug('No time calc available for a new case'); 14 | } 15 | 16 | public void validate(Case customerCase) { 17 | System.debug('validating a new case'); 18 | } 19 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/State/NewCaseState.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/State/OpenCaseStateHandler.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/15/2022. 3 | */ 4 | 5 | public with sharing class OpenCaseStateHandler implements ICaseState { 6 | 7 | 8 | public void close(Case customerCase) { 9 | System.debug('close open case'); 10 | } 11 | 12 | public void calculateTimeSpent(Case customerCase) { 13 | System.debug('calculating time spent on case from date time it was set to new '); 14 | } 15 | 16 | public void validate(Case customerCase) { 17 | System.debug('Validate open case'); 18 | } 19 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/State/OpenCaseStateHandler.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/TestingUtils.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Taken from James Simone's excellent DML Mocks 3 | * https://github.com/jamessimone/apex-dml-mocking/blob/main/force-app/utils/TestingUtils.cls 4 | */ 5 | 6 | public abstract class TestingUtils { 7 | private static Integer startingNumber = 1; 8 | 9 | public static String generateId(Schema.SObjectType sObjectType) { 10 | String result = String.valueOf(startingNumber++); 11 | System.debug(sObjectType.getDescribe().getKeyPrefix()); 12 | return sObjectType.getDescribe().getKeyPrefix() + '0'.repeat(12 - result.length()) + result; 13 | } 14 | 15 | public static SObject generateId(SObject objectInstance) { 16 | if (objectInstance.Id == null) { 17 | objectInstance.Id = generateId(objectInstance.getSObjectType()); 18 | } 19 | return objectInstance; 20 | } 21 | 22 | public static void generateIds(List records) { 23 | for (SObject record : records) { 24 | if (record.Id == null) { 25 | generateId(record); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /force-app/main/default/classes/TestingUtils.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/TriggerHandler.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/TriggerHandler_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/IMusicVisitor.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/13/2022. 3 | */ 4 | 5 | public interface IMusicVisitor { 6 | 7 | void visit(Music.Rock music); 8 | void visit(Music.Pop music); 9 | void visit(Music.ElectronicMusic music); 10 | void visit(Music.PopRock music); 11 | 12 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/IMusicVisitor.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/Music.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/13/2022. 3 | */ 4 | 5 | public abstract class Music { 6 | protected String title; 7 | protected String album; 8 | 9 | public abstract String getMusicDetail(); 10 | public abstract void accept(IMusicVisitor visitor); 11 | 12 | public class Rock extends Music { 13 | 14 | public Rock(String title, String album){ 15 | this.title = title; 16 | this.album = album; 17 | } 18 | 19 | 20 | public override String getMusicDetail() { 21 | return 'rock music: Title ' + title + ' Album ' + album; 22 | } 23 | 24 | 25 | public override void accept(IMusicVisitor visitor) { 26 | visitor.visit(this); 27 | } 28 | } 29 | 30 | public class ElectronicMusic extends Music { 31 | 32 | public ElectronicMusic(String title, String album){ 33 | this.title = title; 34 | this.album = album; 35 | } 36 | 37 | 38 | public override String getMusicDetail() { 39 | return 'Electronic music: Title ' + title + ' Album ' + album; 40 | } 41 | 42 | 43 | public override void accept(IMusicVisitor visitor) { 44 | visitor.visit(this); 45 | } 46 | } 47 | 48 | public virtual class Pop extends Music { 49 | 50 | public Pop(String title, String album) { 51 | this.title = title; 52 | this.album = album; 53 | } 54 | 55 | 56 | public virtual override String getMusicDetail() { 57 | return 'Pop music: Title ' + title + ' Album ' + album; 58 | } 59 | 60 | 61 | public virtual override void accept(IMusicVisitor visitor) { 62 | visitor.visit(this); 63 | } 64 | } 65 | public class PopRock extends Pop{ 66 | protected String title; 67 | protected String Album; 68 | 69 | public PopRock(String title, String Album) { 70 | super(title, Album); 71 | this.title = title; 72 | this.Album = Album; 73 | } 74 | public override String getMusicDetail() { 75 | return 'Pop Rock music: Title ' + title + ' Album ' + album; 76 | } 77 | 78 | 79 | public override void accept(IMusicVisitor visitor) { 80 | visitor.visit(this); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/Music.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/MusicPriceCalculator.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/13/2022. 3 | * This version will fail because poprock extends pop.. 4 | */ 5 | 6 | public with sharing class MusicPriceCalculator { 7 | public static void calculatePrice(Music.Pop m) { 8 | System.debug('Calculate price of Pop Music') ; 9 | } 10 | 11 | public static void calculatePrice(Music.Rock m) { 12 | System.debug('Calculate price of Rock Music') ; 13 | } 14 | 15 | public static void calculatePrice(Music.ElectronicMusic m) { 16 | System.debug('Calculate price of Electronic music'); 17 | 18 | } 19 | 20 | public static void calculatePrice(Music.PopRock m){ 21 | System.debug('Calculate price of PopRock Music') ; 22 | } 23 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/MusicPriceCalculator.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/MusicVisitor.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/13/2022. 3 | */ 4 | 5 | public with sharing class MusicVisitor implements IMusicVisitor { 6 | public Double finalPrice = 0; 7 | 8 | public void visit(Music.Rock music) { 9 | System.debug('calculating rock music'); 10 | finalPrice += 2; 11 | } 12 | 13 | public void visit(Music.Pop music) { 14 | System.debug('Calculating pop music'); 15 | finalPrice += 3; 16 | } 17 | 18 | public void visit(Music.ElectronicMusic music) { 19 | System.debug('calculating electronic music'); 20 | finalPrice += 4; 21 | } 22 | 23 | public void visit(Music.PopRock music) { 24 | System.debug('calculating pop rock music'); 25 | finalPrice += 2.5; 26 | } 27 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/MusicVisitor.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/VisitorTest.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 6/13/2022. 3 | */ 4 | 5 | @IsTest 6 | public with sharing class VisitorTest { 7 | 8 | @IsTest 9 | static void testVisitor(){ 10 | List musicList = new List(); 11 | musicList.add(new Music.Rock('Go Johnny Go', 'Chuck Berry is On Top')); 12 | musicList.add(new Music.Pop('When Doves Cry', 'Price')); 13 | musicList.add(new Music.ElectronicMusic('No idea', 'Make Beleive')); 14 | musicList.add(new Music.PopRock('Sweet Home Alabama', 'Second Helping')); 15 | 16 | MusicVisitor visitor = new MusicVisitor(); 17 | for (Music music : musicList){ 18 | music.accept(visitor); 19 | } 20 | System.assert(visitor.finalPrice == 11.5); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/Visitor/VisitorTest.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/problem_solving_patterns/FrequencyCounter.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 7/31/2022. 3 | */ 4 | 5 | public with sharing class FrequencyCounter { 6 | 7 | public static Map countDuplicates(List strings) { 8 | // 'hello', 'world', 'dog', 'cat', 'taco', 'dog', 'cat', 'dog' 9 | Map countByWords = new Map(); 10 | 11 | for (String word : strings) { 12 | if (countByWords.containsKey(word)) { 13 | countByWords.put(word, countByWords.get(word) + 1); 14 | } else { 15 | countByWords.put(word, 1); 16 | } 17 | } 18 | 19 | String globalString = ''; 20 | Integer globalMax = 1; 21 | for (String str : countByWords.keySet()) { 22 | if (countByWords.get(str) > globalMax) { 23 | globalString = str; 24 | globalMax = countByWords.get(str); 25 | } 26 | 27 | } 28 | return new Map{ 29 | globalString => globalMax 30 | }; 31 | } 32 | 33 | public static List twoSum(List values, Integer target) { 34 | // values = [2,7,11,15], target = 9 35 | // 9 - 2 = 7 36 | // map 7 => 0 37 | Map complementMap = new Map(); 38 | for (Integer i = 0; i < values.size(); i++) { 39 | if (complementMap.containsKey(values[i])) { 40 | return new List{ 41 | complementMap.get(values[i]), i 42 | }; 43 | } else { 44 | Integer complement = target - values[i]; 45 | complementMap.put(complement, i); 46 | } 47 | } 48 | return null; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/problem_solving_patterns/FrequencyCounter.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/problem_solving_patterns/FrequencyCounter_Tests.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 7/31/2022. 3 | */ 4 | 5 | @IsTest 6 | public with sharing class FrequencyCounter_Tests { 7 | 8 | @isTest 9 | static void itShouldCountMostFrequent() { 10 | //given 11 | //note on conditions it is assumed there will always be a winner 12 | List words = new List{ 13 | 'hello', 'world', 'dog', 'cat', 'taco', 'taco', 'dog', 'cat', 'dog' 14 | }; 15 | //when 16 | Map mostFrequentByCount = FrequencyCounter.countDuplicates(words); 17 | //then 18 | System.assert(mostFrequentByCount.get('dog') == 3); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/problem_solving_patterns/FrequencyCounter_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/singleton/HotLeadSingleton.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * @description A simple implementation of a singleton class. 3 | * 4 | */ 5 | public with sharing class HotLeadSingleton { 6 | public List hotleads { get; set; } 7 | private static HotLeadSingleton instance; 8 | 9 | /** 10 | * @description The private constructor is at the core of the pattern. The object can not be created externally, but 11 | * only through the getInstance method 12 | */ 13 | private HotLeadSingleton() { 14 | hotleads = getLeads(); 15 | } 16 | 17 | /** 18 | * @return An instance of the object 19 | * @description The getInstance method checks to see if the private member property "instance" is null. 20 | * If so it returns the cached object. If it has yet been instantiated it creates a new object 21 | */ 22 | public static HotLeadSingleton getInstance() { 23 | if (instance == null) { 24 | instance = new HotLeadSingleton(); 25 | } 26 | return instance; 27 | } 28 | 29 | private List getLeads() { 30 | return [ 31 | SELECT Id, Is_Hot__c 32 | FROM Lead 33 | WHERE Is_Hot__c = TRUE 34 | LIMIT 10 35 | ]; 36 | } 37 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/singleton/HotLeadSingleton.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/singleton/HotLeadSingleton_Tests.cls: -------------------------------------------------------------------------------- 1 | 2 | @IsTest 3 | private class HotLeadSingleton_Tests { 4 | 5 | @IsTest 6 | static void itShouldQueryLeadOnce() { 7 | Lead lead = new Lead(Is_Hot__c = true, FirstName = 'Test', Company = 'Acme Co', 8 | LastName = 'lead', Email = 'test@test.comes'); 9 | insert lead; 10 | 11 | Test.startTest(); 12 | HotLeadSingleton instance = HotLeadSingleton.getInstance(); 13 | System.assert(instance.hotleads.size() == 1); 14 | //Now lets see if one query had been run 15 | System.assert(Limits.getQueries() == 1, 'There should have been one query'); 16 | //Lets call it again 17 | HotLeadSingleton instanceTwo = HotLeadSingleton.getInstance(); 18 | //It should have returned the same instance of the HotLeadSingleton object with out running another SOQL 19 | //Query 20 | System.assert(Limits.getQueries() == 1, 'It should remain one'); 21 | Test.stopTest(); 22 | } 23 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/singleton/HotLeadSingleton_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/singleton/IFeatureToggle.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by brooks.johnson on 7/8/2022. 3 | */ 4 | 5 | public interface IFeatureToggle { 6 | 7 | Boolean isActive(String toggleName); 8 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/singleton/IFeatureToggle.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/contentassets/codey.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bdJohnson72/Design-Patterns-In-Apex/aad3c7a402f3e39a1d45c747b5a8a8acc00e4198/force-app/main/default/contentassets/codey.asset -------------------------------------------------------------------------------- /force-app/main/default/contentassets/codey.asset-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | en_US 5 | codey 6 | 7 | 8 | VIEWER 9 | 10 | 11 | 12 | 13 | 1 14 | codey.png 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /force-app/main/default/customMetadata/Chicago_Pizza.cheese.md-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | cheese 4 | false 5 | 6 | Class_To_Instantiate__c 7 | ChicagoStyleCheese 8 | 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/customMetadata/Chicago_Pizza.pepperoni.md-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | pepperoni 4 | false 5 | 6 | Class_To_Instantiate__c 7 | ChicagoStylePepperoni 8 | 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/customMetadata/Chicago_Pizza.sausage.md-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | sausage 4 | false 5 | 6 | Class_To_Instantiate__c 7 | ChicagoStyleSausage 8 | 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/customMetadata/Pizza.cheese.md-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Cheese 4 | false 5 | 6 | Class_To_Instantiate__c 7 | CheesePizza 8 | 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/customMetadata/Pizza.pepperoni.md-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | pepperoni 5 | false 6 | 7 | Class_To_Instantiate__c 8 | PepperoniPizza 9 | 10 | 11 | -------------------------------------------------------------------------------- /force-app/main/default/customMetadata/Pizza.taco.md-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | taco 4 | false 5 | 6 | Class_To_Instantiate__c 7 | TacoPizza 8 | 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/flexipages/Design_Patterns_UtilityBar.flexipage-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | utilityItems 5 | Region 6 | 7 | 8 | backgroundComponents 9 | Background 10 | 11 | Design Patterns UtilityBar 12 | 13 | one:utilityBarTemplateDesktop 14 | 15 | isLeftAligned 16 | true 17 | 18 | 19 | UtilityBar 20 | 21 | -------------------------------------------------------------------------------- /force-app/main/default/flexipages/Observer_Pattern.flexipage-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | productSubscribe 7 | 8 | 9 | main 10 | Region 11 | 12 | Observer Pattern 13 | 14 | flexipage:defaultAppHomeTemplate 15 | 16 | AppPage 17 | 18 | -------------------------------------------------------------------------------- /force-app/main/default/layouts/Chicago_Pizza__mdt-Chicago Pizza Layout.layout-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | false 6 | true 7 | Information 8 | 9 | 10 | Required 11 | MasterLabel 12 | 13 | 14 | Required 15 | DeveloperName 16 | 17 | 18 | Required 19 | Class_To_Instantiate__c 20 | 21 | 22 | 23 | 24 | Edit 25 | IsProtected 26 | 27 | 28 | Required 29 | NamespacePrefix 30 | 31 | 32 | 33 | 34 | 35 | false 36 | false 37 | true 38 | System Information 39 | 40 | 41 | Readonly 42 | CreatedById 43 | 44 | 45 | 46 | 47 | Readonly 48 | LastModifiedById 49 | 50 | 51 | 52 | 53 | 54 | false 55 | false 56 | false 57 | 58 | 59 | 60 | false 61 | false 62 | false 63 | false 64 | false 65 | 66 | -------------------------------------------------------------------------------- /force-app/main/default/layouts/Pizza__mdt-Pizza Layout.layout-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | false 6 | true 7 | Information 8 | 9 | 10 | Required 11 | MasterLabel 12 | 13 | 14 | Required 15 | DeveloperName 16 | 17 | 18 | Required 19 | Class_To_Instantiate__c 20 | 21 | 22 | 23 | 24 | Edit 25 | IsProtected 26 | 27 | 28 | Required 29 | NamespacePrefix 30 | 31 | 32 | 33 | 34 | 35 | false 36 | false 37 | true 38 | System Information 39 | 40 | 41 | Readonly 42 | CreatedById 43 | 44 | 45 | 46 | 47 | Readonly 48 | LastModifiedById 49 | 50 | 51 | 52 | 53 | 54 | false 55 | false 56 | false 57 | 58 | 59 | 60 | false 61 | false 62 | false 63 | false 64 | false 65 | 66 | -------------------------------------------------------------------------------- /force-app/main/default/layouts/Product_Catalog__c-Product Catelog Layout.layout-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | false 6 | true 7 | Information 8 | 9 | 10 | Readonly 11 | Name 12 | 13 | 14 | Edit 15 | Is_Available__c 16 | 17 | 18 | Edit 19 | Product_Name__c 20 | 21 | 22 | 23 | 24 | Edit 25 | OwnerId 26 | 27 | 28 | 29 | 30 | 31 | false 32 | false 33 | true 34 | System Information 35 | 36 | 37 | Readonly 38 | CreatedById 39 | 40 | 41 | 42 | 43 | Readonly 44 | LastModifiedById 45 | 46 | 47 | 48 | 49 | 50 | false 51 | false 52 | true 53 | 54 | 55 | 56 | 57 | 58 | 59 | NAME 60 | Product_Notification__c.Product_Catelog__c 61 | 62 | false 63 | false 64 | false 65 | false 66 | false 67 | 68 | -------------------------------------------------------------------------------- /force-app/main/default/layouts/Product_Notification__c-Product Notification Layout.layout-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | false 6 | true 7 | Information 8 | 9 | 10 | Readonly 11 | Name 12 | 13 | 14 | Edit 15 | Product_Catelog__c 16 | 17 | 18 | Edit 19 | Send_Email__c 20 | 21 | 22 | Edit 23 | Subscribe__c 24 | 25 | 26 | Edit 27 | Email_Address__c 28 | 29 | 30 | 31 | 32 | Edit 33 | OwnerId 34 | 35 | 36 | 37 | 38 | 39 | false 40 | false 41 | true 42 | System Information 43 | 44 | 45 | Readonly 46 | CreatedById 47 | 48 | 49 | 50 | 51 | Readonly 52 | LastModifiedById 53 | 54 | 55 | 56 | 57 | 58 | false 59 | false 60 | true 61 | 62 | 63 | 64 | 65 | 66 | false 67 | false 68 | false 69 | false 70 | false 71 | 72 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@salesforce/eslint-config-lwc/recommended", "prettier"], 3 | "overrides": [ 4 | { 5 | "files": ["*.test.js"], 6 | "rules": { 7 | "@lwc/lwc/no-unexpected-wire-adapter-usages": "off" 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/productSubscribe/productSubscribe.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {product.Product_Name__c} is 12 | not available yet 13 | 14 | 15 | Would you like to be notified when it is available 16 | 17 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/productSubscribe/productSubscribe.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/12/2022. 3 | */ 4 | 5 | import {LightningElement, wire} from 'lwc'; 6 | import subscribeUser from '@salesforce/apex/ObserverPatternController.subscribeUser' 7 | import getProducts from '@salesforce/apex/ObserverPatternController.getProducts' 8 | import {ShowToastEvent} from 'lightning/platformShowToastEvent'; 9 | 10 | 11 | export default class ProductSubscribe extends LightningElement { 12 | product; 13 | 14 | @wire(getProducts) 15 | wiredProduct({error, data}) { 16 | if (data) { 17 | this.product = data[0]; 18 | console.log(JSON.stringify(this.product)); 19 | } 20 | if (error) { 21 | console.error(error.message); 22 | } 23 | } 24 | 25 | async subscribeUser() { 26 | try { 27 | const input = this.template.querySelector('lightning-input'); 28 | console.log(input) 29 | await subscribeUser({productId: this.product.Id, email: input.value}) 30 | input.value = ''; 31 | this.showToast(); 32 | } catch (e) { 33 | console.error(e.message) 34 | } 35 | } 36 | 37 | showToast() { 38 | const event = new ShowToastEvent({ 39 | message: 'Cool!! We will let you know when it is available' 40 | }); 41 | this.dispatchEvent(event); 42 | } 43 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/productSubscribe/productSubscribe.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Product Subscribe 5 | true 6 | Product Subscribe 7 | 8 | lightning__AppPage 9 | lightning__HomePage 10 | lightning__RecordPage 11 | 12 | 13 | -------------------------------------------------------------------------------- /force-app/main/default/objectTranslations/Product_Catalog__c-en_US/Is_Available__c.fieldTranslation-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Is_Available__c 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/objectTranslations/Product_Catalog__c-en_US/Product_Catalog__c-en_US.objectTranslation-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | Product Catalog 6 | 7 | 8 | true 9 | Product Catalogs 10 | 11 | Consonant 12 | 13 | -------------------------------------------------------------------------------- /force-app/main/default/objectTranslations/Product_Catalog__c-en_US/Product_Name__c.fieldTranslation-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Product_Name__c 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Account/fields/Admin_Only__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Admin_Only__c 4 | false 5 | false 6 | Admin Only 7 | false 8 | Checkbox 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Chicago_Pizza__mdt/Chicago_Pizza__mdt.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | For a reflective factory 4 | Chicago Pizza 5 | Chicago Pizza 6 | Public 7 | 8 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Chicago_Pizza__mdt/fields/Class_To_Instantiate__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Class_To_Instantiate__c 4 | The name of the class to be instantiated by our Type class 5 | false 6 | DeveloperControlled 7 | Class To Instantiate 8 | 100 9 | true 10 | Text 11 | false 12 | 13 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Lead/fields/Is_Hot__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Is_Hot__c 4 | false 5 | Used as an example in the Singleton Pattern 6 | false 7 | Is Hot 8 | false 9 | Checkbox 10 | 11 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Pizza__mdt/Pizza__mdt.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Used in our reflective pizza factory 4 | Pizza 5 | Pizzas 6 | Public 7 | 8 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Pizza__mdt/fields/Class_To_Instantiate__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Class_To_Instantiate__c 4 | Name of the object that our factory will return an instance of. 5 | false 6 | DeveloperControlled 7 | Class To Instantiate 8 | 100 9 | true 10 | Text 11 | false 12 | 13 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Catalog__c/Product_Catalog__c.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Accept 5 | Default 6 | 7 | 8 | Accept 9 | Large 10 | Default 11 | 12 | 13 | Accept 14 | Small 15 | Default 16 | 17 | 18 | CancelEdit 19 | Default 20 | 21 | 22 | CancelEdit 23 | Large 24 | Default 25 | 26 | 27 | CancelEdit 28 | Small 29 | Default 30 | 31 | 32 | Clone 33 | Default 34 | 35 | 36 | Clone 37 | Large 38 | Default 39 | 40 | 41 | Clone 42 | Small 43 | Default 44 | 45 | 46 | Delete 47 | Default 48 | 49 | 50 | Delete 51 | Large 52 | Default 53 | 54 | 55 | Delete 56 | Small 57 | Default 58 | 59 | 60 | Edit 61 | Default 62 | 63 | 64 | Edit 65 | Large 66 | Default 67 | 68 | 69 | Edit 70 | Small 71 | Default 72 | 73 | 74 | List 75 | Default 76 | 77 | 78 | List 79 | Large 80 | Default 81 | 82 | 83 | List 84 | Small 85 | Default 86 | 87 | 88 | New 89 | Default 90 | 91 | 92 | New 93 | Large 94 | Default 95 | 96 | 97 | New 98 | Small 99 | Default 100 | 101 | 102 | SaveEdit 103 | Default 104 | 105 | 106 | SaveEdit 107 | Large 108 | Default 109 | 110 | 111 | SaveEdit 112 | Small 113 | Default 114 | 115 | 116 | Tab 117 | Default 118 | 119 | 120 | Tab 121 | Large 122 | Default 123 | 124 | 125 | Tab 126 | Small 127 | Default 128 | 129 | 130 | View 131 | Default 132 | 133 | 134 | View 135 | Large 136 | Default 137 | 138 | 139 | View 140 | Small 141 | Default 142 | 143 | false 144 | SYSTEM 145 | Deployed 146 | Used to illustrate the Observer Pattern 147 | false 148 | true 149 | false 150 | false 151 | false 152 | false 153 | true 154 | true 155 | true 156 | Private 157 | Product Catalog 158 | 159 | PC-{000000} 160 | Product Catelog Name 161 | AutoNumber 162 | 163 | Product Catalogs 164 | 165 | ReadWrite 166 | Public 167 | 168 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Catalog__c/fields/Is_Available__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Is_Available__c 4 | false 5 | Notify users when product is available. Used in observer pattern 6 | false 7 | Is Available 8 | false 9 | Checkbox 10 | 11 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Catalog__c/fields/Product_Name__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Product_Name__c 4 | Used in observer pattern 5 | false 6 | Product Name 7 | 100 8 | false 9 | false 10 | Text 11 | false 12 | 13 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Catalog__c/listViews/All.listView-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | All 4 | Everything 5 | All 6 | 7 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Notification__c/Product_Notification__c.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Accept 5 | Default 6 | 7 | 8 | Accept 9 | Large 10 | Default 11 | 12 | 13 | Accept 14 | Small 15 | Default 16 | 17 | 18 | CancelEdit 19 | Default 20 | 21 | 22 | CancelEdit 23 | Large 24 | Default 25 | 26 | 27 | CancelEdit 28 | Small 29 | Default 30 | 31 | 32 | Clone 33 | Default 34 | 35 | 36 | Clone 37 | Large 38 | Default 39 | 40 | 41 | Clone 42 | Small 43 | Default 44 | 45 | 46 | Delete 47 | Default 48 | 49 | 50 | Delete 51 | Large 52 | Default 53 | 54 | 55 | Delete 56 | Small 57 | Default 58 | 59 | 60 | Edit 61 | Default 62 | 63 | 64 | Edit 65 | Large 66 | Default 67 | 68 | 69 | Edit 70 | Small 71 | Default 72 | 73 | 74 | List 75 | Default 76 | 77 | 78 | List 79 | Large 80 | Default 81 | 82 | 83 | List 84 | Small 85 | Default 86 | 87 | 88 | New 89 | Default 90 | 91 | 92 | New 93 | Large 94 | Default 95 | 96 | 97 | New 98 | Small 99 | Default 100 | 101 | 102 | SaveEdit 103 | Default 104 | 105 | 106 | SaveEdit 107 | Large 108 | Default 109 | 110 | 111 | SaveEdit 112 | Small 113 | Default 114 | 115 | 116 | Tab 117 | Default 118 | 119 | 120 | Tab 121 | Large 122 | Default 123 | 124 | 125 | Tab 126 | Small 127 | Default 128 | 129 | 130 | View 131 | Default 132 | 133 | 134 | View 135 | Large 136 | Default 137 | 138 | 139 | View 140 | Small 141 | Default 142 | 143 | false 144 | SYSTEM 145 | Deployed 146 | Used in Observer Pattern to notify users 147 | false 148 | true 149 | false 150 | false 151 | false 152 | false 153 | false 154 | true 155 | true 156 | Private 157 | Product Notification 158 | 159 | PN-{0000000} 160 | Product Notification Name 161 | AutoNumber 162 | 163 | Product Notifications 164 | 165 | ReadWrite 166 | Public 167 | 168 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Notification__c/fields/Email_Address__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Email_Address__c 4 | false 5 | Email Address 6 | false 7 | false 8 | Email 9 | false 10 | 11 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Notification__c/fields/Product_Catelog__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Product_Catelog__c 4 | SetNull 5 | false 6 | Product Catalog 7 | Product_Catalog__c 8 | Product Notifications 9 | Product_Notifications 10 | false 11 | false 12 | Lookup 13 | 14 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Notification__c/fields/Send_Email__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Send_Email__c 4 | false 5 | false 6 | Send Email 7 | false 8 | Checkbox 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Notification__c/fields/Subscribe__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Subscribe__c 4 | false 5 | false 6 | Subscribe 7 | false 8 | Checkbox 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Product_Notification__c/listViews/All.listView-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | All 4 | Everything 5 | All 6 | 7 | -------------------------------------------------------------------------------- /force-app/main/default/permissionsets/Design_Patterns.permissionset-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Design_Patterns 5 | true 6 | 7 | 8 | true 9 | Chicago_Pizza__mdt 10 | 11 | 12 | true 13 | Pizza__mdt 14 | 15 | 16 | true 17 | Lead.Is_Hot__c 18 | true 19 | 20 | 21 | true 22 | Product_Catalog__c.Is_Available__c 23 | true 24 | 25 | 26 | true 27 | Product_Catalog__c.Product_Name__c 28 | true 29 | 30 | 31 | true 32 | Product_Notification__c.Email_Address__c 33 | true 34 | 35 | 36 | true 37 | Product_Notification__c.Product_Catelog__c 38 | true 39 | 40 | 41 | true 42 | Product_Notification__c.Send_Email__c 43 | true 44 | 45 | 46 | true 47 | Product_Notification__c.Subscribe__c 48 | true 49 | 50 | false 51 | Design Patterns 52 | 53 | false 54 | true 55 | true 56 | true 57 | true 58 | Lead 59 | true 60 | 61 | 62 | true 63 | true 64 | true 65 | true 66 | true 67 | Product_Catalog__c 68 | true 69 | 70 | 71 | true 72 | true 73 | true 74 | true 75 | true 76 | Product_Notification__c 77 | true 78 | 79 | 80 | -------------------------------------------------------------------------------- /force-app/main/default/tabs/Observer_Pattern.tab-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created by Lightning App Builder 4 | Observer_Pattern 5 | Observer Pattern 6 | Custom4: Hexagon 7 | 8 | -------------------------------------------------------------------------------- /force-app/main/default/tabs/Product_Catalog__c.tab-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | Custom55: Books 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/tabs/Product_Notification__c.tab-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | Custom25: Alarm clock 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/triggers/ProductCatalog.trigger: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Brooks Johnson on 3/27/2022. 3 | */ 4 | 5 | trigger ProductCatalog on Product_Catalog__c (before insert, before update, before delete, 6 | after insert, after update, after delete, after undelete) { 7 | 8 | ProductCatalogTriggerHandler handler = new ProductCatalogTriggerHandler(); 9 | handler.run(); 10 | 11 | } -------------------------------------------------------------------------------- /force-app/main/default/triggers/ProductCatalog.trigger-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const { jestConfig } = require('@salesforce/sfdx-lwc-jest/config'); 2 | 3 | module.exports = { 4 | ...jestConfig, 5 | modulePathIgnorePatterns: ['/.localdevserver'] 6 | }; 7 | -------------------------------------------------------------------------------- /manifest/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | * 5 | ApexClass 6 | 7 | 8 | * 9 | ApexComponent 10 | 11 | 12 | * 13 | ApexPage 14 | 15 | 16 | * 17 | ApexTestSuite 18 | 19 | 20 | * 21 | ApexTrigger 22 | 23 | 24 | * 25 | AuraDefinitionBundle 26 | 27 | 28 | * 29 | LightningComponentBundle 30 | 31 | 32 | * 33 | StaticResource 34 | 35 | 52.0 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "salesforce-app", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Salesforce App", 6 | "scripts": { 7 | "lint": "npm run lint:lwc && npm run lint:aura", 8 | "lint:aura": "eslint **/aura/**", 9 | "lint:lwc": "eslint **/lwc/**", 10 | "test": "npm run test:unit", 11 | "test:unit": "sfdx-lwc-jest", 12 | "test:unit:watch": "sfdx-lwc-jest --watch", 13 | "test:unit:debug": "sfdx-lwc-jest --debug", 14 | "test:unit:coverage": "sfdx-lwc-jest --coverage", 15 | "prettier": "prettier --write \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"", 16 | "prettier:verify": "prettier --list-different \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"", 17 | "postinstall": "husky install" 18 | 19 | }, 20 | "devDependencies": { 21 | "@lwc/eslint-plugin-lwc": "^1.0.1", 22 | "@prettier/plugin-xml": "^0.13.1", 23 | "@salesforce/eslint-config-lwc": "^2.0.0", 24 | "@salesforce/eslint-plugin-aura": "^2.0.0", 25 | "@salesforce/eslint-plugin-lightning": "^0.1.1", 26 | "@salesforce/sfdx-lwc-jest": "^0.13.0", 27 | "eslint": "^7.29.0", 28 | "eslint-config-prettier": "^8.3.0", 29 | "eslint-plugin-import": "^2.23.4", 30 | "eslint-plugin-jest": "^24.3.6", 31 | "husky": "^6.0.0", 32 | "lint-staged": "^11.0.0", 33 | "prettier": "^2.3.1", 34 | "prettier-plugin-apex": "^1.9.1" 35 | }, 36 | "lint-staged": { 37 | "**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}": [ 38 | "prettier --write" 39 | ], 40 | "**/{aura,lwc}/**": [ 41 | "eslint" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /recipes-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bdJohnson72/Design-Patterns-In-Apex/aad3c7a402f3e39a1d45c747b5a8a8acc00e4198/recipes-logo.png -------------------------------------------------------------------------------- /scripts/apex/hello.apex: -------------------------------------------------------------------------------- 1 | // Use .apex files to store anonymous Apex. 2 | // You can execute anonymous Apex in VS Code by selecting the 3 | // apex text and running the command: 4 | // SFDX: Execute Anonymous Apex with Currently Selected Text 5 | // You can also execute the entire file by running the command: 6 | // SFDX: Execute Anonymous Apex with Editor Contents 7 | 8 | string tempvar = 'Enter_your_name_here'; 9 | System.debug('Hello World!'); 10 | System.debug('My name is ' + tempvar); -------------------------------------------------------------------------------- /scripts/soql/account.soql: -------------------------------------------------------------------------------- 1 | // Use .soql files to store SOQL queries. 2 | // You can execute queries in VS Code by selecting the 3 | // query text and running the command: 4 | // SFDX: Execute SOQL Query with Currently Selected Text 5 | 6 | SELECT Id, Name FROM Account 7 | -------------------------------------------------------------------------------- /sfdx-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageDirectories": [ 3 | { 4 | "path": "force-app", 5 | "default": true 6 | } 7 | ], 8 | "name": "Design_Patterns_In_Apex", 9 | "namespace": "", 10 | "sfdcLoginUrl": "https://login.salesforce.com", 11 | "sourceApiVersion": "52.0" 12 | } 13 | --------------------------------------------------------------------------------
11 | {product.Product_Name__c} is 12 | not available yet 13 |
15 | Would you like to be notified when it is available 16 |