Apexデザインパターン

Salesforce Developers Japan
Salesforce Developers JapanSalesforce Developers Japan
Apex のデザインパターン
開発者向けセッション
河村  嘉之
テクニカルソリューションアーキテクト
Safe Harbor
 Safe harbor statement under the Private Securities Litigation Reform Act of 1995:

 This presentation may contain forward-looking statements that involve risks, uncertainties, and assumptions. If any such uncertainties
 materialize or if any of the assumptions proves incorrect, the results of salesforce.com, inc. could differ materially from the results
 expressed or implied by the forward-looking statements we make. All statements other than statements of historical fact could be
 deemed forward-looking, including any projections of product or service availability, subscriber growth, earnings, revenues, or other
 financial items and any statements regarding strategies or plans of management for future operations, statements of belief, any
 statements concerning new, planned, or upgraded services or technology developments and customer contracts or use of our services.

 The risks and uncertainties referred to above include – but are not limited to – risks associated with developing and delivering new
 functionality for our service, new products and services, our new business model, our past operating losses, possible fluctuations in our
 operating results and rate of growth, interruptions or delays in our Web hosting, breach of our security measures, the outcome of
 intellectual property and other litigation, risks associated with possible mergers and acquisitions, the immature market in which we
 operate, our relatively limited operating history, our ability to expand, retain, and motivate our employees and manage our growth, new
 releases of our service and successful customer deployment, our limited history reselling non-salesforce.com products, and utilization
 and selling to larger enterprise customers. Further information on potential factors that could affect the financial results of salesforce.com,
 inc. is included in our annual report on Form 10-Q for the most recent fiscal quarter ended July 31, 2012. This documents and others
 containing important disclosures are available on the SEC Filings section of the Investor Information section of our Web site.

 Any unreleased services or features referenced in this or other presentations, press releases or public statements are not currently
 available and may not be delivered on time or at all. Customers who purchase our services should make the purchase decisions based
 upon features that are currently available. Salesforce.com, inc. assumes no obligation and does not intend to update these forward-
 looking statements.
セッションの進め⽅方
6 つのデザインパターンを扱う
 §  問題
 §  パターンの概要
 §  コード
積極的な発⾔言を歓迎します!
6 つのパターン
1.  ?
2.  ?
3.  ?
4.  ?
5.  ?
6.  ?
システム管理理者のビリーの場合

      •  Salesforce の管理理を担当して 3 年年
      •  コーディングはつい最近始めたばかり
      •  エラーのせいで落落ち込んでいます

        Trigger.AccountTrigger: line 3, column 1
        System.LimitException: Too many record
        type describes: 101


      •  助けが必要!
問題のコード
01   trigger AccountTrigger on Account (before insert, before update) {
02       for(Account record : Trigger.new){
03           AccountFooRecordType rt = new AccountFooRecordType();
04           ....
05       }
06   }

07   public class AccountFooRecordType {
08       public String id {get;private set;}
09       public AccountFooRecordType(){
10           id = Account.sObjectType.getDescribe()
11               .getRecordTypeInfosByName().get('Foo').getRecordTypeId();
12       }
13   }



     •  1 件の Account (取引先) レコードが挿⼊入されたらどうなるか?
     •  200 件以上の Account が挿⼊入されたら…?
ソリューション – Singleton




                         Singleton

              - instance : Singleton

              - Singleton()
              + getInstance() : Singleton
コードを書いてみる
01   trigger AccountTrigger on Account (before insert, before update) {
02       for(Account record : Trigger.new){
03           AccountFooRecordType rt = new AccountFooRecordType();
04           ....
05       }
06   }

07   public class AccountFooRecordType {
08       public String id {get;private set;}
09       public AccountFooRecordType(){
10           id = Account.sObjectType.getDescribe()
11               .getRecordTypeInfosByName().get('Foo').getRecordTypeId();
12       }
13   }



     •  これは先ほどと同じコード
     •  変更更点は次のスライドでハイライト表⽰示します
コードを書いてみる
01   trigger AccountTrigger on Account (before insert, before update) {
02       for(Account record : Trigger.new){
03           AccountFooRecordType rt = (new AccountFooRecordType()).getInstance();
04           ....
05       }
06   }

07   public class AccountFooRecordType {
08       public String id {get;private set;}
09       public AccountFooRecordType(){
10           id = Account.sObjectType.getDescribe()
11               .getRecordTypeInfosByName().get('Foo').getRecordTypeId();
12       }
13       public AccountFooRecordType getInstance(){
14          return new AccountFooRecordType();
15       }
16   }
コードを書いてみる
01   trigger AccountTrigger on Account (before insert, before update) {
02       for(Account record : Trigger.new){
03           AccountFooRecordType rt = AccountFooRecordType.getInstance();
04           ....
05       }
06   }

07   public class AccountFooRecordType {
08       public String id {get;private set;}
09       public AccountFooRecordType(){
10           id = Account.sObjectType.getDescribe()
11               .getRecordTypeInfosByName().get('Foo').getRecordTypeId();
12       }
13       public static AccountFooRecordType getInstance(){
14          return new AccountFooRecordType();
15       }
16   }
static

 •  メンバー変数、メソッド、ブロックを修飾可能
 •  実⾏行行のタイミングは?
     •    クラスがランタイム環境にロードされるとき

 •  Java で保持される期間は?
     •    JVM が実⾏行行されている間

 •  Apex では?
     •    トランザクションが実⾏行行されている間
コードを書いてみる
01   trigger AccountTrigger on Account (before insert, before update) {
02       for(Account record : Trigger.new){
03           AccountFooRecordType rt = AccountFooRecordType.getInstance();
04           ....
05       }
06   }

07   public class AccountFooRecordType {
08       public String id {get;private set;}
09       public AccountFooRecordType(){
10           id = Account.sObjectType.getDescribe()
11               .getRecordTypeInfosByName().get('Foo').getRecordTypeId();
12       }
13       public static AccountFooRecordType getInstance(){
14          return new AccountFooRecordType();
15       }
16   }
コードを書いてみる
01   trigger AccountTrigger on Account (before insert, before update) {
02       for(Account record : Trigger.new){
03           AccountFooRecordType rt = AccountFooRecordType.getInstance();
04           ....
05       }
06   }

07   public class AccountFooRecordType {
08       public static AccountFooRecordType instance = null;
09       public String id {get;private set;}
10       public AccountFooRecordType(){
11           id = Account.sObjectType.getDescribe()
12               .getRecordTypeInfosByName().get('Foo').getRecordTypeId();
13       }
14       public static AccountFooRecordType getInstance(){
15           if(instance == null) instance = new AccountFooRecordType();
16           return instance;
17       }
18   }
コードを書いてみる
01   trigger AccountTrigger on Account (before insert, before update) {
02       for(Account record : Trigger.new){
03           AccountFooRecordType rt = AccountFooRecordType.getInstance();
04           ....
05       }
06   }

07   public class AccountFooRecordType {
08       private static AccountFooRecordType instance = null;
09       public String id {get;private set;}
10       public AccountFooRecordType(){
11           id = Account.sObjectType.getDescribe()
12               .getRecordTypeInfosByName().get('Foo').getRecordTypeId();
13       }
14       public static AccountFooRecordType getInstance(){
15           if(instance == null) instance = new AccountFooRecordType();
16           return instance;
17       }
18   }
コードを書いてみる
01   trigger AccountTrigger on Account (before insert, before update) {
02       for(Account record : Trigger.new){
03           AccountFooRecordType rt = AccountFooRecordType.getInstance();
04           ....
05       }
06   }

07   public class AccountFooRecordType {
08       private static AccountFooRecordType instance = null;
09       public String id {get;private set;}
10       private AccountFooRecordType(){
11           id = Account.sObjectType.getDescribe()
12               .getRecordTypeInfosByName().get('Foo').getRecordTypeId();
13       }
14       public static AccountFooRecordType getInstance(){
15           if(instance == null) instance = new AccountFooRecordType();
16           return instance;
17       }
18   }
6 つのパターン
1.  Singleton
2.  ?
3.  ?
4.  ?
5.  ?
6.  ?
geocode('moscone center')
//=> 37.7845935,-122.3994262
ビリーに提案したコードはこちら
01   public class GoogleMapsGeocoder{
02       public static List<Double> getLatLong(String address){
03           //web service callout of some sort
04           return new List<Double>{0,0};
05       }
06   }

07   System.debug(GoogleMapsGeocoder.getLatLong('moscone center'));
08   //=> 13:56:36.029 (29225000)|USER_DEBUG|[29]|DEBUG|(0.0, 0.0)




      すると、ビリーはこんな要件を挙げてきました
      §  Google マップのほかにも選択肢が必要
      §  クライアントコードでプロバイダを選べるようにしたい
ソリューション – Strategy
                                        Context            strategies           Strategy
               Client
                                   +operation()                             +operation()
                                                       1
                                                                        *
 構造的なアルゴリズムを定義し、
                                                       ConcreteStrategyA                        ConcreteStrategyB
 各アルゴリズムをカプセル化して、
                                                  +operation()                             +operation()
 相互に切切り替え可能にする
 §  Context => Geocoder
 §  operation() => getLatLong()
 §  Strategy => GeocodeService
 §  ConcreteStrategyA => GoogleMapsImpl
 §  ConcreteStrategyB => MapQuestImpl
コードを書いてみる
01   public interface GeocodeService{
02       List<Double> getLatLong(String address);
03   }



04   public class GoogleMapsImpl implements GeocodeService{
05       public List<Double> getLatLong(String address){
06           return new List<Double>{0,0};
07       }
08   }


09   public class MapQuestImpl implements GeocodeService{
10       public List<Double> getLatLong(String address){
11           return new List<Double>{1,1};
12       }
13   }
01   public class Geocoder {
02       private GeocodeService strategy;
03       public Geocoder(GeocodeService s){
04          strategy = s;
05       }
06       public List<Double> getLatLong(String address){
07          return strategy.getLatLong(address);
08       }
09   }

                                           Geocoder
                                                             strategies                 GeocodeService
             Client         + Geocoder(GeocodeService)
                                                         1                       +getLatLong(String)
                            +getLatLong(String)                           *

                                                                      GoogleMapsImpl                           MapQuestImpl

                                                               +getLatLong(String)                     +getLatLong(String)



10   Geocoder geocoder = new Geocoder(new GoogleMapsImpl());
11   System.debug(geocoder.getLatLong('moscone center'));
12   //=> 13:56:36.029 (29225000)|USER_DEBUG|[29]|DEBUG|(0.0, 0.0)
01   public class Geocoder {
02       public static final Map<String,Strategy> strategies = new
03           Map<String,Strategy>{'googlemaps' => new GoogleMapsImpl()
04                               ,'mapquest'   => new MapQuestImpl()};
05       private GeocodeService strategy;
06       public Geocoder(String name){ strategy = strategies.get(name);}
07       public List<Double> getLatLong(String address){
08          return strategy.getLatLong(address);
09       }
10   }
                                         Geocoder
                                                        strategies                 GeocodeService
          Client          + Geocoder(String)
                                                    1                       +getLatLong(String)
                          +getLatLong(String)                        *

                                                                 GoogleMapsImpl                           MapQuestImpl

                                                          +getLatLong(String)                     +getLatLong(String)


11   Geocoder geocoder = new Geocoder('googlemaps');
12   System.debug(geocoder.getLatLong('moscone center'));
13   //=> 13:56:36.029 (29225000)|USER_DEBUG|[29]|DEBUG|(0.0, 0.0)
01   public class Geocoder {
02       public class NameException extends Exception{}
03       public static final Map<String,GeocodeService> strategies;
04       static{
05           GlobalVariable__c gv = GlobalVariable__c.getInstance('strategies');
06           List<String> strategyNames = new List<String>();
07           if(gv != null && gv.value__c != null) strategyNames = gv.value__c.split(',');
08           strategies = new Map<String,GeocodeService>();
09           for(String name : strategyNames){
10               try{strategies.put(name,(GeocodeService)Type.forName(name+'impl').newInstance());}
11               catch(Exception e){continue;} //skip bad name silently
12           }
13       }
14       private GeocodeService strategy;
15       public Geocoder(String name){
16           if(!strategies.containsKey(name)) throw new NameException(name);
17           strategy = strategies.get(name);
18       }
19       public List<Double> getLatLong(String address){
20           return strategy.getLatLong(address);
21       }
22   }
6 つのパターン
1.  Singleton
2.  Strategy
3.  ?
4.  ?
5.  ?
6.  ?
次の問題は?
Apex で項⽬目を追加せずに、sObject の機能を拡張するにはどうすればよいか?




                                 処理理の途中で選択の
                                チェックボックスを表⽰示




                                               更更新にもとづいて
                                                項⽬目の値を計算
ソリューション – sObject Decorator
                                                            sObject


                                                 + Save()
                                                 ...




                                                    Concrete sObject
                        sObjectDecorator
                                                 + Field1
         VF        + sObj: sObject               + Field2
      Controller   + Property : type             ...
                                                 + Fieldn
                   + sObjectDecorator(sObject)
                   + Operation()                 + Save()
                                                 ...




                   (「ラッパークラス」と呼ばれる)
サンプルのシナリオ
天気の情報を格納する sObject 「Weather」
 §  「City__c」(市)
 §  「TempInFahrenheit__c」(華⽒氏換算の気温)

Visualforce ページ作成にあたっての要件
 §  Temperature レコードに格納されている華⽒氏の気温データを、摂⽒氏で表⽰示する
 §  Temperature レコードに摂⽒氏で⼊入⼒力力される気温データを、華⽒氏のデータとして保存する




                                                   双⽅方向で
                                                データを換算して表⽰示
コード – Decorator による sObject クラスの拡張
    01   public class DecoratedWeather {
    02
    03        public Weather__c weather { get; private set; }
    04
    05        public DecoratedWeather (Weather__c weather) {
    06               this.weather = weather;
    07        }
    08
    09        public Decimal tempInCelcius {
    10             get {
    11                   if (weather != null && tempInCelcius == null )
    12                        tempInCelcius = new Temperature().FtoC(weather.TempInFahrenheit__c);
    13
    14                     return tempInCelcius;
    15             }
    16             set {
    17                     if (weather != null && value != null )
    18                          weather.TempInFahrenheit__c= new Temperature().CtoF(value);
    19
    20             tempInCelcius = value;
    21             }
    22        }
    23   }
コード – カスタムコントローラ
01   public class Weather_Controller {
02
03       public List<DecoratedWeather> listOfWeather {
04
05                    get {
06                            if (listOfWeather == null) {
07                                     listOfWeather = new List<DecoratedWeather>();
08
09                    for (Weather__c weather : [select name, temperature__c from Weather__c]) {
10                                              listOfWeather.add(new DecoratedWeather(weather));
11                    }
12                    }
13                    return listOfWeather;
14                    }
15
16                    private set;
17           }
18   }
コード – Visualforce ページ
 01   <apex:page controller="weather_controller">
 02
 03            <!-- VF page to render the weather records with Ajax that only rerenders
 04                      the page on change of the temperature
 05            -->
 06       <apex:form id="theForm">
 07           <apex:pageBlock id="pageBlock">
 08                <apex:pageBlockTable value="{!listOfWeather}" var="weather">
 09                    <apex:column value="{!weather.weather.name}"/>
 10                    <apex:column headerValue="Temperature (C)">
 11                        <apex:actionRegion >
 12                            <apex:inputText value="{!weather.tempInCelcius}">
 13                                <apex:actionSupport event="onchange"
 14                                                reRender="pageBlock"/>
 15                            </apex:inputText>
 16                        </apex:actionRegion>
 17                    </apex:column>
 18                    <apex:column headerValue="Temperature (F)"
 19                                    value="{!weather.weather.Temperature__c}"
 20                                    id="tempInF"/>
 21                </apex:pageBlockTable>                                             クライアントサイドの
 22                     </apex:pageBlock>                                              ロジックは不不使⽤用!!!
 23       </apex:form>
 24   </apex:page>
完成した  Visualforce ページ
6 つのパターン
1.  Singleton
2.  Strategy
3.  sObject Decorater
4.  ?
5.  ?
6.  ?
このコードの問題は?

Visualforce コントローラ 1   Visualforce コントローラ 2




                       Visualforce コントローラ 3
ソリューション – Façade
       Client1                                                  Foo1



                                  FooFacade              + Foo1() : String



                            + LotsOfFoo() : String
       Client2                                                  Foo2


                                                         + Foo2() : String




             01   public String LotsOfFoo() {
             02          Foo1 f1 = new Foo1();
             03          Foo2 f2 = new Foo2();
             04
             05          return f1.Foo1() + f2.Foo2();
             06   }
サンプルのシナリオ
Customer (顧客) レコードを作成する、複数の処理理から成るトランザクション
  §  CreateAccount Web サービス – 取引先レコードを作成
  §  CreateContact Web サービス – 取引先責任者レコードを作成
⼊入⼒力力
  §  UserId – 現在のユーザ
  §  Timestamp – 現在のタイムスタンプ
  §  Name – 取引先名 (⼤大⽂文字に変換)
  §  LastName、FirstName – 取引先責任者の姓、名
その他に設定すべき項⽬目
  §  Timeout – タイムアウト値
  §  Hostname – ホスト名
リモートシステムから取引先と取引先責任者の追跡番号が返される
コード – Façade クラス
  01   public class CreateCustomerFacade {
  02
  03       public class CreateCustomerResponse {
  04           public String accountNumber;
  05           public String contactNumber;
  06
  07             public CreateCustomerResponse(String accountNumber,
  08                                        String contactNumber) {
  09                 this.accountNumber = accountNumber;
  10                 this.contactNumber = contactNumber;
  11             }
  12       }
  13       ...
コード – Façade クラス
  01      ...
  02   public String CreateCustomerExternal(String Name,
  03                                  String LastName, String FirstName) {
  04             CreateAccount_Service.CreateAccount stubCA =
  05                                         new CreateAccount_Service.CreateAccount();
  06             CreateAccount_Service.Inputs inputCA =
  07                                         new CreateAccount_Service.Inputs();
  08
  09             stubCA.timeout_x = 60000;
  10             stubCA.endpoint_x = 'https://www.foo.com/ca';
  11
  12             inputCA.userid = Userinfo.getUserName();
  13             inputCA.timestamp = datetime.now();
  14             inputCA.Name = name.toUpperCase();
  15
  16             String accountNumber = inputCA.CreateAccount(input);
  17
  18             /* REPEAT FOR CONTACT */
  19
  20             return new CreateCustomerResponse (
  21                                         accountNumber, contactNumber);
  22   }
コード – クライアント

01   public class FooController{
02
03       public Account account { get; set; }
04       public Contact contact { get; set; }
05       …
         public void CallCreateCustomerWS() {
06
             CreateCustomerFacade ca =
07
                   new CreateCustomerFacade();
08
09           CreateCustomerFacade.CreateCustomerResponse resp =
10                                                    ca.CreateCustomerExternal(account.name,
11   contact.LastName, contact.FirstName);
12
13           account.accountNumber = resp.accountNumber;
14           contact.contactNumber__c = resp.contactNumber;
15       }
     }
6 つのパターン
1.  Singleton
2.  Strategy
3.  sObject Decorater
4.  Façade
5.  ?
6.  ?
式をどうやって表現するか?

1 AND 2
1 OR (2 AND 3)
(1 AND 2) OR ((3 OR 4) AND 5)
ソリューション – Composite                                                                       Component

                                                                                 +operation()                              children
                                                     Client                      +add(Component)
                                                                                 +remove(Component)
                                                                                 +get(Integer)
           1 AND 2
 Client                and : Composite

                                                                                 Leaf                          Composite
                   1 : Leaf       2 : Leaf
                                                                        +operation()               +operation()
                                                                                                   +add(Component)
                                                                                                   +remove(Component)
                                                                                                   +get(Integer)


             1 OR (2 AND 3)                                   (1 AND 2) OR ((3 OR 4) AND 5)
          Client                  or                           Client                         or


                              1            and                                      and                    and


                                       2         3                           1            2           or          5


                                                                                               3           4
コードを書いてみる
01   public interface Expression {
02       Expression add(Expression expr);
03       Expression set(String name, Boolean value);
04       Boolean evaluate();
05   }

06   public abstract class Composite implements Expression{
07       public List<Expression> children {get; private set;}
08       public Composite(){ this.children = new List<Expression>(); }
09       public Expression add(Expression expr){
10           children.add(expr); return this;
11       }
12       public Expression set(String name, Boolean value){
13           for(Expression expr : children) expr.set(name,value);
14           return this;
15       }
16       public abstract Boolean evaluate();
17       public Boolean hasChildren{get{ return !children.isEmpty(); }}
18   }
01   public class AndComposite extends Composite{
02       public override Boolean evaluate(){
03           for(Expression expr : children) if(!expr.evaluate()) return false;
04           return true;
05       }
06   }

07   public class OrComposite extends Composite{
08       public override Boolean evaluate(){
09           for(Expression expr : children) if(expr.evaluate()) return true;
10           return false;
11       }
12   }

13 public class Variable implements Expression{
14     public String name {get;private set;}
15     public Boolean value {get;private set;}
16     public Variable(String name){ this.name = name; }
17     public Expression add(Expression expr){ return this; }
18     public Expression set(String name, Boolean value){
19         if(this.name != null && this.name.equalsIgnoreCase(name))
20             this.value = value;
21         return this;
22     }
23     public Boolean evaluate(){ return value; }
24 }
式を作成する
01   //1 AND 2
02   Expression expr = new AndComposite();
03   expr.add(new Variable('1'));
04   expr.add(new Variable('2'));

05   //1 OR (2 AND 3)
06   Expression expr = new OrComposite();
07   expr.add(new Variable('1'));
08   Expression expr2 = new AndComposite();
09   expr.add(expr2);
10   expr2.add(new Variable('2'));
11   expr2.add(new Variable('3'));

12   //no need for expr2 var if using "method chaining"
13   //last line of add method: return this;
14   Expression expr = (new OrComposite())
15       .add(new Variable('1'))
16       .add((new AndComposite())
17           .add(new Variable('2'))
18           .add(new Variable('3'))
19       );
式の使⽤用例例
01   //1 OR (2 AND 3)
02   Expression expr = (new OrComposite())
03       .add(new Variable('1'))
04       .add((new AndComposite())
05           .add(new Variable('2'))
06           .add(new Variable('3'))
07       )
08       .set('1',false)
09       .set('2',true)
10       .set('3',false);
11
12   System.debug(expr.evaluate());
13   //FALSE OR (TRUE AND FALSE) => FALSE
14
15   expr.set('3',true);
16
17   System.debug(expr.evaluate());
18   //FALSE OR (TRUE AND TRUE) => TRUE
6 つのパターン
1.  Singleton
2.  Strategy
3.  sObject Decorater
4.  Façade
5.  Composite
6.  ?
ビリーが新たな問題に直⾯面しています
Opportunity (商談) レコードがクローズされたタイミングで
Order (注⽂文) レコードを作成するトリガを記述したつもりだったが…
 §  クローズ済みの商談レコードが更更新されるたびに注⽂文レコードが作成されてしまう
 §  Apex データローダを使ってインポートを実⾏行行した場合に、
     クローズ済みの商談レコードの⼀一部で注⽂文レコードが作成されないことがある
問題のコード
01   trigger OpptyTrigger on Opportunity (after insert, after update) {
02
03          if (trigger.new[0].isClosed) {
04                 Order__c order = new Order__c();
05                 …                                                  前の状態がどうであるかに
                                                                       関係なく実⾏行行される
06                 insert order
07          }                                  バルク処理理に
08   }                                        対応していない



                  再利利⽤用性が低い
こうしたらどうか?
01   trigger OpptyTrigger on Opportunity (after insert, after update) {
02
03          new OrderClass().CreateOrder(trigger.new);
04
05   }

01   public class OrderClass {
02
03          public void CreateOrder(List<Opportunity> opptyList) {
04                 for (Opportunity oppty : opptyList) {
05                        if (oppty.isClosed) {
06                               Order__c order = new Order__c();
07                               ...
08                               insert order;                       前の状態がどうであるかに
                                                                      関係なく実⾏行行される
09                        }
10                 }
11
12          }                     バルク処理理に
13   }                           対応していない
ではこうしたら?
 01    trigger OpptyTrigger on Opportunity (before insert, before update) {
 02           if (trigger.isInsert) {
 03                  new OrderClass().CreateOrder(trigger.newMap, null);
 04           else
0506                 new OrderClass().CreateOrder(trigger.newMap, trigger.oldMap);

 01  public class OrderClass {
 02
 03         public void CreateOrder(Map<Opportunity> opptyMapNew
 04                                                    Map<Opportunity> opptyMapOld) {
 05                List<Order__c> orderList = new List<Order__c>();
 06                for (Opportunity oppty : opptyMapNew.values()) {
 07                       if (oppty.isClosed && (opptyMapOld == null ||
 08                                     !opptyMapOld.get(oppty.id).isClosed)) {
バルク処理理は
 09                              Order__c order = new Order__c();
OK  になった
 10                              ...
 11                              orderList.add(order);
 12                       }                                          コードのコンテキスト依存度度が
 13                }                                                    ⾼高く、再利利⽤用が難しい
 14                insert orderList ;
 15         }
ソリューション – "Bulk State Transition" (バルク状態遷移)

                                      Utility Class

             Trigger

                                  + Foo(sObject[])




      •    状態が変更更されたレコードの中で、         •    あらゆるコンテキストから
           条件に⼀一致するものを検出                  呼び出せるユーティリティクラスの
      •    所定の処理理を実⾏行行するユーティリティ           汎⽤用的なメソッド
           クラスのメソッドを呼び出す
      •    条件を満たすレコードのみを
           ユーティリティクラスに渡す
これで問題解決!!!
01   trigger OpptyTrigger on Opportunity (after insert, after update) {
02          if (trigger.isAfter && (trigger.isInsert || trigger.isUpdate)) {
03                 List<Opportunity> closedOpptyList = new List<Opportunity>();
04                 for (Opportunity oppty : trigger.new) {
05                        if (oppty.isClosed && (trigger.isInsert ||
06                                      (trigger.isUpdate &&
07                                      !trigger.oldMap.get(oppty.id).isClosed)))   トリガが状態遷移を
08                               closedOpptyList.add(oppty);                          適切切に処理理
09                 }
10                 if (!closedOpptyList.isEmpty())
11                        new OrderClass().CreateOrderFromOpptys(closedOpptyList)

01   public class OrderClass {
02          public void CreateOrdersFromOpptys(List<Opportunity> opptyList) {
03                 List<Order__c> orderList = new List<Order__c>();
04                 for (Opportunity oppty : opptyList) {
05                        Order__c order = new Order__c();
06                        ...                                                 再利利⽤用性が⾼高まり、
07                        orderList.add(order);                               バルク処理理にも対応
08                 }
09                 insert orderList ;
6 つのパターン
1.  Singleton
2.  Strategy
3.  sObject Decorater
4.  Façade
5.  Composite
6.  Bulk State Transition
関連リソース
ラッパークラス
http://wiki.developerforce.com/page/Wrapper_Class (英語)

Apex コードに関するベストプラクティス
http://wiki.developerforce.com/page/Apex_Code_Best_Practices (英語)

Apex の Web サービスとコールアウト
http://wiki.developerforce.com/page/JP:Apex_Web_Services_and_Callouts

サンプルコード
https://github.com/richardvanhook/Force.com-Patterns (英語)
Apexデザインパターン
Apexデザインパターン
1 of 57

Recommended

ドメイン駆動設計のためのオブジェクト指向入門 by
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門増田 亨
48K views89 slides
ちいさなオブジェクトでドメインモデルを組み立てる by
ちいさなオブジェクトでドメインモデルを組み立てるちいさなオブジェクトでドメインモデルを組み立てる
ちいさなオブジェクトでドメインモデルを組み立てる増田 亨
15K views26 slides
Force.com Canvas アプリケーション by
Force.com Canvas アプリケーションForce.com Canvas アプリケーション
Force.com Canvas アプリケーションSalesforce Developers Japan
3.6K views19 slides
SalesforceにおけるCDC(変更データキャプチャ)の実装・活用法について by
SalesforceにおけるCDC(変更データキャプチャ)の実装・活用法についてSalesforceにおけるCDC(変更データキャプチャ)の実装・活用法について
SalesforceにおけるCDC(変更データキャプチャ)の実装・活用法についてTakashi Hatamoto
1.8K views17 slides
リッチなドメインモデル 名前探し by
リッチなドメインモデル 名前探しリッチなドメインモデル 名前探し
リッチなドメインモデル 名前探し増田 亨
14.4K views29 slides
SQL Server 使いのための Azure Synapse Analytics - Spark 入門 by
SQL Server 使いのための Azure Synapse Analytics - Spark 入門SQL Server 使いのための Azure Synapse Analytics - Spark 入門
SQL Server 使いのための Azure Synapse Analytics - Spark 入門Daiyu Hatakeyama
1.3K views68 slides

More Related Content

What's hot

Einstein Analyticsによるユースケース別機能、実現例のご紹介(前編)〜Winter’18 新機能紹介を交えて by
Einstein Analyticsによるユースケース別機能、実現例のご紹介(前編)〜Winter’18 新機能紹介を交えてEinstein Analyticsによるユースケース別機能、実現例のご紹介(前編)〜Winter’18 新機能紹介を交えて
Einstein Analyticsによるユースケース別機能、実現例のご紹介(前編)〜Winter’18 新機能紹介を交えてSalesforce Developers Japan
3K views40 slides
イミュータブルデータモデルの極意 by
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意Yoshitaka Kawashima
23.8K views28 slides
オブジェクト指向の設計と実装の学び方のコツ by
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ増田 亨
95.1K views76 slides
ドメインオブジェクトの見つけ方・作り方・育て方 by
ドメインオブジェクトの見つけ方・作り方・育て方ドメインオブジェクトの見つけ方・作り方・育て方
ドメインオブジェクトの見つけ方・作り方・育て方増田 亨
30.6K views95 slides
ドメイン駆動設計 本格入門 by
ドメイン駆動設計 本格入門ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門増田 亨
44.6K views139 slides
オブジェクト指向エクササイズのススメ by
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメYoji Kanno
57.2K views85 slides

What's hot(20)

Einstein Analyticsによるユースケース別機能、実現例のご紹介(前編)〜Winter’18 新機能紹介を交えて by Salesforce Developers Japan
Einstein Analyticsによるユースケース別機能、実現例のご紹介(前編)〜Winter’18 新機能紹介を交えてEinstein Analyticsによるユースケース別機能、実現例のご紹介(前編)〜Winter’18 新機能紹介を交えて
Einstein Analyticsによるユースケース別機能、実現例のご紹介(前編)〜Winter’18 新機能紹介を交えて
イミュータブルデータモデルの極意 by Yoshitaka Kawashima
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意
Yoshitaka Kawashima23.8K views
オブジェクト指向の設計と実装の学び方のコツ by 増田 亨
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ
増田 亨95.1K views
ドメインオブジェクトの見つけ方・作り方・育て方 by 増田 亨
ドメインオブジェクトの見つけ方・作り方・育て方ドメインオブジェクトの見つけ方・作り方・育て方
ドメインオブジェクトの見つけ方・作り方・育て方
増田 亨30.6K views
ドメイン駆動設計 本格入門 by 増田 亨
ドメイン駆動設計 本格入門ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門
増田 亨44.6K views
オブジェクト指向エクササイズのススメ by Yoji Kanno
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメ
Yoji Kanno57.2K views
ドメイン駆動で開発する ラフスケッチから実装まで by 増田 亨
ドメイン駆動で開発する ラフスケッチから実装までドメイン駆動で開発する ラフスケッチから実装まで
ドメイン駆動で開発する ラフスケッチから実装まで
増田 亨15.7K views
オブジェクト指向プログラミングのためのモデリング入門 by 増田 亨
オブジェクト指向プログラミングのためのモデリング入門オブジェクト指向プログラミングのためのモデリング入門
オブジェクト指向プログラミングのためのモデリング入門
増田 亨37K views
文字コードに起因する脆弱性とその対策(増補版) by Hiroshi Tokumaru
文字コードに起因する脆弱性とその対策(増補版)文字コードに起因する脆弱性とその対策(増補版)
文字コードに起因する脆弱性とその対策(増補版)
Hiroshi Tokumaru34K views
Consumer Driven Contractsで REST API/マイクロサービスをテスト #m3tech by Toshiaki Maki
Consumer Driven Contractsで REST API/マイクロサービスをテスト #m3techConsumer Driven Contractsで REST API/マイクロサービスをテスト #m3tech
Consumer Driven Contractsで REST API/マイクロサービスをテスト #m3tech
Toshiaki Maki5.2K views
Sparkによる GISデータを題材とした時系列データ処理 (Hadoop / Spark Conference Japan 2016 講演資料) by Hadoop / Spark Conference Japan
Sparkによる GISデータを題材とした時系列データ処理 (Hadoop / Spark Conference Japan 2016 講演資料)Sparkによる GISデータを題材とした時系列データ処理 (Hadoop / Spark Conference Japan 2016 講演資料)
Sparkによる GISデータを題材とした時系列データ処理 (Hadoop / Spark Conference Japan 2016 講演資料)
Delta Lake with Synapse dataflow by Ryoma Nagata
Delta Lake with Synapse dataflowDelta Lake with Synapse dataflow
Delta Lake with Synapse dataflow
Ryoma Nagata311 views
データモデリング・テクニック by Hidekatsu Izuno
データモデリング・テクニックデータモデリング・テクニック
データモデリング・テクニック
Hidekatsu Izuno17.5K views
モダンなイベント駆動型システム連携を学ぼう〜Platform Events 入門 by Salesforce Developers Japan
モダンなイベント駆動型システム連携を学ぼう〜Platform Events 入門モダンなイベント駆動型システム連携を学ぼう〜Platform Events 入門
モダンなイベント駆動型システム連携を学ぼう〜Platform Events 入門
イミュータブルデータモデル(入門編) by Yoshitaka Kawashima
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
Yoshitaka Kawashima185.9K views
データ基盤の従来~最新の考え方とSynapse Analyticsでの実現 by Ryoma Nagata
データ基盤の従来~最新の考え方とSynapse Analyticsでの実現データ基盤の従来~最新の考え方とSynapse Analyticsでの実現
データ基盤の従来~最新の考え方とSynapse Analyticsでの実現
Ryoma Nagata725 views
Java EE から Quarkus による開発への移行について by Shigeru Tatsuta
Java EE から Quarkus による開発への移行についてJava EE から Quarkus による開発への移行について
Java EE から Quarkus による開発への移行について
Shigeru Tatsuta840 views
ウェブ・セキュリティ基礎試験(徳丸基礎試験)の模擬試験問題 by Hiroshi Tokumaru
ウェブ・セキュリティ基礎試験(徳丸基礎試験)の模擬試験問題ウェブ・セキュリティ基礎試験(徳丸基礎試験)の模擬試験問題
ウェブ・セキュリティ基礎試験(徳丸基礎試験)の模擬試験問題
Hiroshi Tokumaru27.4K views
ドメイン駆動設計という仕事の流儀 by 増田 亨
ドメイン駆動設計という仕事の流儀ドメイン駆動設計という仕事の流儀
ドメイン駆動設計という仕事の流儀
増田 亨16.5K views

Viewers also liked

インターンシップ成果報告会 発表資料 by
インターンシップ成果報告会 発表資料インターンシップ成果報告会 発表資料
インターンシップ成果報告会 発表資料T2C_
73.7K views9 slides
Spring'16 Apex Code 新機能 by
Spring'16 Apex Code 新機能Spring'16 Apex Code 新機能
Spring'16 Apex Code 新機能Taiki Yoshikawa
2.5K views30 slides
Spring'17リリースノート輪読会 API By フレクト by
Spring'17リリースノート輪読会 API By フレクトSpring'17リリースノート輪読会 API By フレクト
Spring'17リリースノート輪読会 API By フレクト政雄 金森
15.5K views60 slides
tokyo-salesforce-dg-meetup-2017-etl-with-sphinx by
tokyo-salesforce-dg-meetup-2017-etl-with-sphinxtokyo-salesforce-dg-meetup-2017-etl-with-sphinx
tokyo-salesforce-dg-meetup-2017-etl-with-sphinxshun saito
15.5K views11 slides
Sf素人が2週間でアプリケーションビルダーに挑戦してみた by
Sf素人が2週間でアプリケーションビルダーに挑戦してみたSf素人が2週間でアプリケーションビルダーに挑戦してみた
Sf素人が2週間でアプリケーションビルダーに挑戦してみた政雄 金森
20.3K views18 slides
Salesforce Development Best Practices by
Salesforce Development Best PracticesSalesforce Development Best Practices
Salesforce Development Best PracticesVivek Chawla
22.9K views40 slides

Viewers also liked(8)

インターンシップ成果報告会 発表資料 by T2C_
インターンシップ成果報告会 発表資料インターンシップ成果報告会 発表資料
インターンシップ成果報告会 発表資料
T2C_73.7K views
Spring'16 Apex Code 新機能 by Taiki Yoshikawa
Spring'16 Apex Code 新機能Spring'16 Apex Code 新機能
Spring'16 Apex Code 新機能
Taiki Yoshikawa2.5K views
Spring'17リリースノート輪読会 API By フレクト by 政雄 金森
Spring'17リリースノート輪読会 API By フレクトSpring'17リリースノート輪読会 API By フレクト
Spring'17リリースノート輪読会 API By フレクト
政雄 金森15.5K views
tokyo-salesforce-dg-meetup-2017-etl-with-sphinx by shun saito
tokyo-salesforce-dg-meetup-2017-etl-with-sphinxtokyo-salesforce-dg-meetup-2017-etl-with-sphinx
tokyo-salesforce-dg-meetup-2017-etl-with-sphinx
shun saito15.5K views
Sf素人が2週間でアプリケーションビルダーに挑戦してみた by 政雄 金森
Sf素人が2週間でアプリケーションビルダーに挑戦してみたSf素人が2週間でアプリケーションビルダーに挑戦してみた
Sf素人が2週間でアプリケーションビルダーに挑戦してみた
政雄 金森20.3K views
Salesforce Development Best Practices by Vivek Chawla
Salesforce Development Best PracticesSalesforce Development Best Practices
Salesforce Development Best Practices
Vivek Chawla22.9K views
Salesforceの導入で押さえておきたいポイント by Taiki Yoshikawa
Salesforceの導入で押さえておきたいポイントSalesforceの導入で押さえておきたいポイント
Salesforceの導入で押さえておきたいポイント
Taiki Yoshikawa29K views
Customizing the salesforce console with the integration toolkit mt by Salesforce Developers
Customizing the salesforce console with the integration toolkit mtCustomizing the salesforce console with the integration toolkit mt
Customizing the salesforce console with the integration toolkit mt

Similar to Apexデザインパターン

Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで by
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」までNeo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」までKeiichiro Seida
7.4K views41 slides
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション) by
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)Akira Kuratani
1.1K views16 slides
Tech talk salesforce mobile sdk by
Tech talk   salesforce mobile sdkTech talk   salesforce mobile sdk
Tech talk salesforce mobile sdkKazuki Nakajima
1.9K views25 slides
システムのモダナイズ 落ちても良いアプリの作り方 by
システムのモダナイズ 落ちても良いアプリの作り方システムのモダナイズ 落ちても良いアプリの作り方
システムのモダナイズ 落ちても良いアプリの作り方Chihiro Ito
2.1K views45 slides
Spring3.1概要 データアクセスとトランザクション処理 by
Spring3.1概要 データアクセスとトランザクション処理Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理土岐 孝平
17.8K views62 slides
Apexコアデベロッパーセミナー070726 配布用 by
Apexコアデベロッパーセミナー070726 配布用Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用stomita
6.3K views57 slides

Similar to Apexデザインパターン(20)

Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで by Keiichiro Seida
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」までNeo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Keiichiro Seida7.4K views
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション) by Akira Kuratani
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)
Akira Kuratani1.1K views
Tech talk salesforce mobile sdk by Kazuki Nakajima
Tech talk   salesforce mobile sdkTech talk   salesforce mobile sdk
Tech talk salesforce mobile sdk
Kazuki Nakajima1.9K views
システムのモダナイズ 落ちても良いアプリの作り方 by Chihiro Ito
システムのモダナイズ 落ちても良いアプリの作り方システムのモダナイズ 落ちても良いアプリの作り方
システムのモダナイズ 落ちても良いアプリの作り方
Chihiro Ito2.1K views
Spring3.1概要 データアクセスとトランザクション処理 by 土岐 孝平
Spring3.1概要 データアクセスとトランザクション処理Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理
土岐 孝平17.8K views
Apexコアデベロッパーセミナー070726 配布用 by stomita
Apexコアデベロッパーセミナー070726 配布用Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用
stomita6.3K views
勉強会force#3 iOSアプリ開発 by Kazuki Nakajima
勉強会force#3 iOSアプリ開発勉強会force#3 iOSアプリ開発
勉強会force#3 iOSアプリ開発
Kazuki Nakajima1.2K views
ApexトリガのBest Practiceを目指して by Takahiro Yonei
ApexトリガのBest Practiceを目指してApexトリガのBest Practiceを目指して
ApexトリガのBest Practiceを目指して
Takahiro Yonei5.9K views
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014 by Takashi Yahata
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
Takashi Yahata3.6K views
Data api workshop at Co-Edo by Yuji Takayama
Data api workshop at Co-EdoData api workshop at Co-Edo
Data api workshop at Co-Edo
Yuji Takayama4.9K views
データマイニング+WEB勉強会資料第6回 by Naoyuki Yamada
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
Naoyuki Yamada3.6K views
Entity Framework 5.0 deep dive by Atsushi Fukui
Entity Framework 5.0 deep diveEntity Framework 5.0 deep dive
Entity Framework 5.0 deep dive
Atsushi Fukui3.1K views
JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」 by Hiroyuki Ohnaka
JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」
JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」
Hiroyuki Ohnaka7.3K views
初めての Data API CMS どうでしょう - 仙台編 - by Yuji Takayama
初めての Data API   CMS どうでしょう - 仙台編 -初めての Data API   CMS どうでしょう - 仙台編 -
初めての Data API CMS どうでしょう - 仙台編 -
Yuji Takayama4.8K views
AppExchangeパートナー&デベロッパー第2部:20070523版 by Junichiro Tasaki
AppExchangeパートナー&デベロッパー第2部:20070523版AppExchangeパートナー&デベロッパー第2部:20070523版
AppExchangeパートナー&デベロッパー第2部:20070523版
Junichiro Tasaki1.4K views
ドメイン駆動設計(DDD)の実践Part2 by 増田 亨
ドメイン駆動設計(DDD)の実践Part2ドメイン駆動設計(DDD)の実践Part2
ドメイン駆動設計(DDD)の実践Part2
増田 亨12.9K views

More from Salesforce Developers Japan

Salesforce DX の始め方とパートナー様成功事例 by
Salesforce DX の始め方とパートナー様成功事例Salesforce DX の始め方とパートナー様成功事例
Salesforce DX の始め方とパートナー様成功事例Salesforce Developers Japan
5.6K views60 slides
Einstein Analyticsでのデータ取り込みと加工 by
Einstein Analyticsでのデータ取り込みと加工Einstein Analyticsでのデータ取り込みと加工
Einstein Analyticsでのデータ取り込みと加工Salesforce Developers Japan
6.1K views41 slides
GMOペパボのエンジニアが語るHeroku活用ノウハウ by
GMOペパボのエンジニアが語るHeroku活用ノウハウGMOペパボのエンジニアが語るHeroku活用ノウハウ
GMOペパボのエンジニアが語るHeroku活用ノウハウSalesforce Developers Japan
1K views42 slides
Salesforce Big Object 最前線 by
Salesforce Big Object 最前線Salesforce Big Object 最前線
Salesforce Big Object 最前線Salesforce Developers Japan
1.2K views32 slides
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜 by
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜Salesforce Developers Japan
1K views56 slides
Einstein Next Best Action を試してみよう by
Einstein Next Best Action を試してみようEinstein Next Best Action を試してみよう
Einstein Next Best Action を試してみようSalesforce Developers Japan
4K views29 slides

More from Salesforce Developers Japan(20)

Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜 by Salesforce Developers Japan
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜
Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発 by Salesforce Developers Japan
Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発
Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発
業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 - by Salesforce Developers Japan
業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -
業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -
AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方 by Salesforce Developers Japan
AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方
AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方

Recently uploaded

定例会スライド_キャチs 公開用.pdf by
定例会スライド_キャチs 公開用.pdf定例会スライド_キャチs 公開用.pdf
定例会スライド_キャチs 公開用.pdfKeio Robotics Association
146 views64 slides
PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」 by
PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」
PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」PC Cluster Consortium
66 views12 slides
パスキーでリードする: NGINXとKeycloakによる効率的な認証・認可 by
パスキーでリードする: NGINXとKeycloakによる効率的な認証・認可パスキーでリードする: NGINXとKeycloakによる効率的な認証・認可
パスキーでリードする: NGINXとKeycloakによる効率的な認証・認可Hitachi, Ltd. OSS Solution Center.
10 views22 slides
光コラボは契約してはいけない by
光コラボは契約してはいけない光コラボは契約してはいけない
光コラボは契約してはいけないTakuya Matsunaga
28 views17 slides
PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」 by
PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」
PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」PC Cluster Consortium
28 views36 slides

Recently uploaded(7)

PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」 by PC Cluster Consortium
PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」
PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」
光コラボは契約してはいけない by Takuya Matsunaga
光コラボは契約してはいけない光コラボは契約してはいけない
光コラボは契約してはいけない
Takuya Matsunaga28 views
PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」 by PC Cluster Consortium
PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」
PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」

Apexデザインパターン

  • 3. Safe Harbor Safe harbor statement under the Private Securities Litigation Reform Act of 1995: This presentation may contain forward-looking statements that involve risks, uncertainties, and assumptions. If any such uncertainties materialize or if any of the assumptions proves incorrect, the results of salesforce.com, inc. could differ materially from the results expressed or implied by the forward-looking statements we make. All statements other than statements of historical fact could be deemed forward-looking, including any projections of product or service availability, subscriber growth, earnings, revenues, or other financial items and any statements regarding strategies or plans of management for future operations, statements of belief, any statements concerning new, planned, or upgraded services or technology developments and customer contracts or use of our services. The risks and uncertainties referred to above include – but are not limited to – risks associated with developing and delivering new functionality for our service, new products and services, our new business model, our past operating losses, possible fluctuations in our operating results and rate of growth, interruptions or delays in our Web hosting, breach of our security measures, the outcome of intellectual property and other litigation, risks associated with possible mergers and acquisitions, the immature market in which we operate, our relatively limited operating history, our ability to expand, retain, and motivate our employees and manage our growth, new releases of our service and successful customer deployment, our limited history reselling non-salesforce.com products, and utilization and selling to larger enterprise customers. Further information on potential factors that could affect the financial results of salesforce.com, inc. is included in our annual report on Form 10-Q for the most recent fiscal quarter ended July 31, 2012. This documents and others containing important disclosures are available on the SEC Filings section of the Investor Information section of our Web site. Any unreleased services or features referenced in this or other presentations, press releases or public statements are not currently available and may not be delivered on time or at all. Customers who purchase our services should make the purchase decisions based upon features that are currently available. Salesforce.com, inc. assumes no obligation and does not intend to update these forward- looking statements.
  • 4. セッションの進め⽅方 6 つのデザインパターンを扱う §  問題 §  パターンの概要 §  コード 積極的な発⾔言を歓迎します!
  • 5. 6 つのパターン 1.  ? 2.  ? 3.  ? 4.  ? 5.  ? 6.  ?
  • 6. システム管理理者のビリーの場合 •  Salesforce の管理理を担当して 3 年年 •  コーディングはつい最近始めたばかり •  エラーのせいで落落ち込んでいます Trigger.AccountTrigger: line 3, column 1 System.LimitException: Too many record type describes: 101 •  助けが必要!
  • 7. 問題のコード 01 trigger AccountTrigger on Account (before insert, before update) { 02 for(Account record : Trigger.new){ 03 AccountFooRecordType rt = new AccountFooRecordType(); 04 .... 05 } 06 } 07 public class AccountFooRecordType { 08 public String id {get;private set;} 09 public AccountFooRecordType(){ 10 id = Account.sObjectType.getDescribe() 11 .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 12 } 13 } •  1 件の Account (取引先) レコードが挿⼊入されたらどうなるか? •  200 件以上の Account が挿⼊入されたら…?
  • 8. ソリューション – Singleton Singleton - instance : Singleton - Singleton() + getInstance() : Singleton
  • 9. コードを書いてみる 01 trigger AccountTrigger on Account (before insert, before update) { 02 for(Account record : Trigger.new){ 03 AccountFooRecordType rt = new AccountFooRecordType(); 04 .... 05 } 06 } 07 public class AccountFooRecordType { 08 public String id {get;private set;} 09 public AccountFooRecordType(){ 10 id = Account.sObjectType.getDescribe() 11 .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 12 } 13 } •  これは先ほどと同じコード •  変更更点は次のスライドでハイライト表⽰示します
  • 10. コードを書いてみる 01 trigger AccountTrigger on Account (before insert, before update) { 02 for(Account record : Trigger.new){ 03 AccountFooRecordType rt = (new AccountFooRecordType()).getInstance(); 04 .... 05 } 06 } 07 public class AccountFooRecordType { 08 public String id {get;private set;} 09 public AccountFooRecordType(){ 10 id = Account.sObjectType.getDescribe() 11 .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 12 } 13 public AccountFooRecordType getInstance(){ 14 return new AccountFooRecordType(); 15 } 16 }
  • 11. コードを書いてみる 01 trigger AccountTrigger on Account (before insert, before update) { 02 for(Account record : Trigger.new){ 03 AccountFooRecordType rt = AccountFooRecordType.getInstance(); 04 .... 05 } 06 } 07 public class AccountFooRecordType { 08 public String id {get;private set;} 09 public AccountFooRecordType(){ 10 id = Account.sObjectType.getDescribe() 11 .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 12 } 13 public static AccountFooRecordType getInstance(){ 14 return new AccountFooRecordType(); 15 } 16 }
  • 12. static •  メンバー変数、メソッド、ブロックを修飾可能 •  実⾏行行のタイミングは? •  クラスがランタイム環境にロードされるとき •  Java で保持される期間は? •  JVM が実⾏行行されている間 •  Apex では? •  トランザクションが実⾏行行されている間
  • 13. コードを書いてみる 01 trigger AccountTrigger on Account (before insert, before update) { 02 for(Account record : Trigger.new){ 03 AccountFooRecordType rt = AccountFooRecordType.getInstance(); 04 .... 05 } 06 } 07 public class AccountFooRecordType { 08 public String id {get;private set;} 09 public AccountFooRecordType(){ 10 id = Account.sObjectType.getDescribe() 11 .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 12 } 13 public static AccountFooRecordType getInstance(){ 14 return new AccountFooRecordType(); 15 } 16 }
  • 14. コードを書いてみる 01 trigger AccountTrigger on Account (before insert, before update) { 02 for(Account record : Trigger.new){ 03 AccountFooRecordType rt = AccountFooRecordType.getInstance(); 04 .... 05 } 06 } 07 public class AccountFooRecordType { 08 public static AccountFooRecordType instance = null; 09 public String id {get;private set;} 10 public AccountFooRecordType(){ 11 id = Account.sObjectType.getDescribe() 12 .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 13 } 14 public static AccountFooRecordType getInstance(){ 15 if(instance == null) instance = new AccountFooRecordType(); 16 return instance; 17 } 18 }
  • 15. コードを書いてみる 01 trigger AccountTrigger on Account (before insert, before update) { 02 for(Account record : Trigger.new){ 03 AccountFooRecordType rt = AccountFooRecordType.getInstance(); 04 .... 05 } 06 } 07 public class AccountFooRecordType { 08 private static AccountFooRecordType instance = null; 09 public String id {get;private set;} 10 public AccountFooRecordType(){ 11 id = Account.sObjectType.getDescribe() 12 .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 13 } 14 public static AccountFooRecordType getInstance(){ 15 if(instance == null) instance = new AccountFooRecordType(); 16 return instance; 17 } 18 }
  • 16. コードを書いてみる 01 trigger AccountTrigger on Account (before insert, before update) { 02 for(Account record : Trigger.new){ 03 AccountFooRecordType rt = AccountFooRecordType.getInstance(); 04 .... 05 } 06 } 07 public class AccountFooRecordType { 08 private static AccountFooRecordType instance = null; 09 public String id {get;private set;} 10 private AccountFooRecordType(){ 11 id = Account.sObjectType.getDescribe() 12 .getRecordTypeInfosByName().get('Foo').getRecordTypeId(); 13 } 14 public static AccountFooRecordType getInstance(){ 15 if(instance == null) instance = new AccountFooRecordType(); 16 return instance; 17 } 18 }
  • 17. 6 つのパターン 1.  Singleton 2.  ? 3.  ? 4.  ? 5.  ? 6.  ?
  • 19. ビリーに提案したコードはこちら 01 public class GoogleMapsGeocoder{ 02 public static List<Double> getLatLong(String address){ 03 //web service callout of some sort 04 return new List<Double>{0,0}; 05 } 06 } 07 System.debug(GoogleMapsGeocoder.getLatLong('moscone center')); 08 //=> 13:56:36.029 (29225000)|USER_DEBUG|[29]|DEBUG|(0.0, 0.0) すると、ビリーはこんな要件を挙げてきました §  Google マップのほかにも選択肢が必要 §  クライアントコードでプロバイダを選べるようにしたい
  • 20. ソリューション – Strategy Context strategies Strategy Client +operation() +operation() 1 * 構造的なアルゴリズムを定義し、 ConcreteStrategyA ConcreteStrategyB 各アルゴリズムをカプセル化して、 +operation() +operation() 相互に切切り替え可能にする §  Context => Geocoder §  operation() => getLatLong() §  Strategy => GeocodeService §  ConcreteStrategyA => GoogleMapsImpl §  ConcreteStrategyB => MapQuestImpl
  • 21. コードを書いてみる 01 public interface GeocodeService{ 02 List<Double> getLatLong(String address); 03 } 04 public class GoogleMapsImpl implements GeocodeService{ 05 public List<Double> getLatLong(String address){ 06 return new List<Double>{0,0}; 07 } 08 } 09 public class MapQuestImpl implements GeocodeService{ 10 public List<Double> getLatLong(String address){ 11 return new List<Double>{1,1}; 12 } 13 }
  • 22. 01 public class Geocoder { 02 private GeocodeService strategy; 03 public Geocoder(GeocodeService s){ 04 strategy = s; 05 } 06 public List<Double> getLatLong(String address){ 07 return strategy.getLatLong(address); 08 } 09 } Geocoder strategies GeocodeService Client + Geocoder(GeocodeService) 1 +getLatLong(String) +getLatLong(String) * GoogleMapsImpl MapQuestImpl +getLatLong(String) +getLatLong(String) 10 Geocoder geocoder = new Geocoder(new GoogleMapsImpl()); 11 System.debug(geocoder.getLatLong('moscone center')); 12 //=> 13:56:36.029 (29225000)|USER_DEBUG|[29]|DEBUG|(0.0, 0.0)
  • 23. 01 public class Geocoder { 02 public static final Map<String,Strategy> strategies = new 03 Map<String,Strategy>{'googlemaps' => new GoogleMapsImpl() 04 ,'mapquest' => new MapQuestImpl()}; 05 private GeocodeService strategy; 06 public Geocoder(String name){ strategy = strategies.get(name);} 07 public List<Double> getLatLong(String address){ 08 return strategy.getLatLong(address); 09 } 10 } Geocoder strategies GeocodeService Client + Geocoder(String) 1 +getLatLong(String) +getLatLong(String) * GoogleMapsImpl MapQuestImpl +getLatLong(String) +getLatLong(String) 11 Geocoder geocoder = new Geocoder('googlemaps'); 12 System.debug(geocoder.getLatLong('moscone center')); 13 //=> 13:56:36.029 (29225000)|USER_DEBUG|[29]|DEBUG|(0.0, 0.0)
  • 24. 01 public class Geocoder { 02 public class NameException extends Exception{} 03 public static final Map<String,GeocodeService> strategies; 04 static{ 05 GlobalVariable__c gv = GlobalVariable__c.getInstance('strategies'); 06 List<String> strategyNames = new List<String>(); 07 if(gv != null && gv.value__c != null) strategyNames = gv.value__c.split(','); 08 strategies = new Map<String,GeocodeService>(); 09 for(String name : strategyNames){ 10 try{strategies.put(name,(GeocodeService)Type.forName(name+'impl').newInstance());} 11 catch(Exception e){continue;} //skip bad name silently 12 } 13 } 14 private GeocodeService strategy; 15 public Geocoder(String name){ 16 if(!strategies.containsKey(name)) throw new NameException(name); 17 strategy = strategies.get(name); 18 } 19 public List<Double> getLatLong(String address){ 20 return strategy.getLatLong(address); 21 } 22 }
  • 25. 6 つのパターン 1.  Singleton 2.  Strategy 3.  ? 4.  ? 5.  ? 6.  ?
  • 26. 次の問題は? Apex で項⽬目を追加せずに、sObject の機能を拡張するにはどうすればよいか? 処理理の途中で選択の チェックボックスを表⽰示 更更新にもとづいて 項⽬目の値を計算
  • 27. ソリューション – sObject Decorator sObject + Save() ... Concrete sObject sObjectDecorator + Field1 VF + sObj: sObject + Field2 Controller + Property : type ... + Fieldn + sObjectDecorator(sObject) + Operation() + Save() ... (「ラッパークラス」と呼ばれる)
  • 28. サンプルのシナリオ 天気の情報を格納する sObject 「Weather」 §  「City__c」(市) §  「TempInFahrenheit__c」(華⽒氏換算の気温) Visualforce ページ作成にあたっての要件 §  Temperature レコードに格納されている華⽒氏の気温データを、摂⽒氏で表⽰示する §  Temperature レコードに摂⽒氏で⼊入⼒力力される気温データを、華⽒氏のデータとして保存する 双⽅方向で データを換算して表⽰示
  • 29. コード – Decorator による sObject クラスの拡張 01 public class DecoratedWeather { 02 03 public Weather__c weather { get; private set; } 04 05 public DecoratedWeather (Weather__c weather) { 06 this.weather = weather; 07 } 08 09 public Decimal tempInCelcius { 10 get { 11 if (weather != null && tempInCelcius == null ) 12 tempInCelcius = new Temperature().FtoC(weather.TempInFahrenheit__c); 13 14 return tempInCelcius; 15 } 16 set { 17 if (weather != null && value != null ) 18 weather.TempInFahrenheit__c= new Temperature().CtoF(value); 19 20 tempInCelcius = value; 21 } 22 } 23 }
  • 30. コード – カスタムコントローラ 01 public class Weather_Controller { 02 03 public List<DecoratedWeather> listOfWeather { 04 05 get { 06 if (listOfWeather == null) { 07 listOfWeather = new List<DecoratedWeather>(); 08 09 for (Weather__c weather : [select name, temperature__c from Weather__c]) { 10 listOfWeather.add(new DecoratedWeather(weather)); 11 } 12 } 13 return listOfWeather; 14 } 15 16 private set; 17 } 18 }
  • 31. コード – Visualforce ページ 01 <apex:page controller="weather_controller"> 02 03 <!-- VF page to render the weather records with Ajax that only rerenders 04 the page on change of the temperature 05 --> 06 <apex:form id="theForm"> 07 <apex:pageBlock id="pageBlock"> 08 <apex:pageBlockTable value="{!listOfWeather}" var="weather"> 09 <apex:column value="{!weather.weather.name}"/> 10 <apex:column headerValue="Temperature (C)"> 11 <apex:actionRegion > 12 <apex:inputText value="{!weather.tempInCelcius}"> 13 <apex:actionSupport event="onchange" 14 reRender="pageBlock"/> 15 </apex:inputText> 16 </apex:actionRegion> 17 </apex:column> 18 <apex:column headerValue="Temperature (F)" 19 value="{!weather.weather.Temperature__c}" 20 id="tempInF"/> 21 </apex:pageBlockTable> クライアントサイドの 22 </apex:pageBlock> ロジックは不不使⽤用!!! 23 </apex:form> 24 </apex:page>
  • 33. 6 つのパターン 1.  Singleton 2.  Strategy 3.  sObject Decorater 4.  ? 5.  ? 6.  ?
  • 34. このコードの問題は? Visualforce コントローラ 1 Visualforce コントローラ 2 Visualforce コントローラ 3
  • 35. ソリューション – Façade Client1 Foo1 FooFacade + Foo1() : String + LotsOfFoo() : String Client2 Foo2 + Foo2() : String 01 public String LotsOfFoo() { 02 Foo1 f1 = new Foo1(); 03 Foo2 f2 = new Foo2(); 04 05 return f1.Foo1() + f2.Foo2(); 06 }
  • 36. サンプルのシナリオ Customer (顧客) レコードを作成する、複数の処理理から成るトランザクション §  CreateAccount Web サービス – 取引先レコードを作成 §  CreateContact Web サービス – 取引先責任者レコードを作成 ⼊入⼒力力 §  UserId – 現在のユーザ §  Timestamp – 現在のタイムスタンプ §  Name – 取引先名 (⼤大⽂文字に変換) §  LastName、FirstName – 取引先責任者の姓、名 その他に設定すべき項⽬目 §  Timeout – タイムアウト値 §  Hostname – ホスト名 リモートシステムから取引先と取引先責任者の追跡番号が返される
  • 37. コード – Façade クラス 01 public class CreateCustomerFacade { 02 03 public class CreateCustomerResponse { 04 public String accountNumber; 05 public String contactNumber; 06 07 public CreateCustomerResponse(String accountNumber, 08 String contactNumber) { 09 this.accountNumber = accountNumber; 10 this.contactNumber = contactNumber; 11 } 12 } 13 ...
  • 38. コード – Façade クラス 01 ... 02 public String CreateCustomerExternal(String Name, 03 String LastName, String FirstName) { 04 CreateAccount_Service.CreateAccount stubCA = 05 new CreateAccount_Service.CreateAccount(); 06 CreateAccount_Service.Inputs inputCA = 07 new CreateAccount_Service.Inputs(); 08 09 stubCA.timeout_x = 60000; 10 stubCA.endpoint_x = 'https://www.foo.com/ca'; 11 12 inputCA.userid = Userinfo.getUserName(); 13 inputCA.timestamp = datetime.now(); 14 inputCA.Name = name.toUpperCase(); 15 16 String accountNumber = inputCA.CreateAccount(input); 17 18 /* REPEAT FOR CONTACT */ 19 20 return new CreateCustomerResponse ( 21 accountNumber, contactNumber); 22 }
  • 39. コード – クライアント 01 public class FooController{ 02 03 public Account account { get; set; } 04 public Contact contact { get; set; } 05 … public void CallCreateCustomerWS() { 06 CreateCustomerFacade ca = 07 new CreateCustomerFacade(); 08 09 CreateCustomerFacade.CreateCustomerResponse resp = 10 ca.CreateCustomerExternal(account.name, 11 contact.LastName, contact.FirstName); 12 13 account.accountNumber = resp.accountNumber; 14 contact.contactNumber__c = resp.contactNumber; 15 } }
  • 40. 6 つのパターン 1.  Singleton 2.  Strategy 3.  sObject Decorater 4.  Façade 5.  ? 6.  ?
  • 41. 式をどうやって表現するか? 1 AND 2 1 OR (2 AND 3) (1 AND 2) OR ((3 OR 4) AND 5)
  • 42. ソリューション – Composite Component +operation() children Client +add(Component) +remove(Component) +get(Integer) 1 AND 2 Client and : Composite Leaf Composite 1 : Leaf 2 : Leaf +operation() +operation() +add(Component) +remove(Component) +get(Integer) 1 OR (2 AND 3) (1 AND 2) OR ((3 OR 4) AND 5) Client or Client or 1 and and and 2 3 1 2 or 5 3 4
  • 43. コードを書いてみる 01 public interface Expression { 02 Expression add(Expression expr); 03 Expression set(String name, Boolean value); 04 Boolean evaluate(); 05 } 06 public abstract class Composite implements Expression{ 07 public List<Expression> children {get; private set;} 08 public Composite(){ this.children = new List<Expression>(); } 09 public Expression add(Expression expr){ 10 children.add(expr); return this; 11 } 12 public Expression set(String name, Boolean value){ 13 for(Expression expr : children) expr.set(name,value); 14 return this; 15 } 16 public abstract Boolean evaluate(); 17 public Boolean hasChildren{get{ return !children.isEmpty(); }} 18 }
  • 44. 01 public class AndComposite extends Composite{ 02 public override Boolean evaluate(){ 03 for(Expression expr : children) if(!expr.evaluate()) return false; 04 return true; 05 } 06 } 07 public class OrComposite extends Composite{ 08 public override Boolean evaluate(){ 09 for(Expression expr : children) if(expr.evaluate()) return true; 10 return false; 11 } 12 } 13 public class Variable implements Expression{ 14 public String name {get;private set;} 15 public Boolean value {get;private set;} 16 public Variable(String name){ this.name = name; } 17 public Expression add(Expression expr){ return this; } 18 public Expression set(String name, Boolean value){ 19 if(this.name != null && this.name.equalsIgnoreCase(name)) 20 this.value = value; 21 return this; 22 } 23 public Boolean evaluate(){ return value; } 24 }
  • 45. 式を作成する 01 //1 AND 2 02 Expression expr = new AndComposite(); 03 expr.add(new Variable('1')); 04 expr.add(new Variable('2')); 05 //1 OR (2 AND 3) 06 Expression expr = new OrComposite(); 07 expr.add(new Variable('1')); 08 Expression expr2 = new AndComposite(); 09 expr.add(expr2); 10 expr2.add(new Variable('2')); 11 expr2.add(new Variable('3')); 12 //no need for expr2 var if using "method chaining" 13 //last line of add method: return this; 14 Expression expr = (new OrComposite()) 15 .add(new Variable('1')) 16 .add((new AndComposite()) 17 .add(new Variable('2')) 18 .add(new Variable('3')) 19 );
  • 46. 式の使⽤用例例 01 //1 OR (2 AND 3) 02 Expression expr = (new OrComposite()) 03 .add(new Variable('1')) 04 .add((new AndComposite()) 05 .add(new Variable('2')) 06 .add(new Variable('3')) 07 ) 08 .set('1',false) 09 .set('2',true) 10 .set('3',false); 11 12 System.debug(expr.evaluate()); 13 //FALSE OR (TRUE AND FALSE) => FALSE 14 15 expr.set('3',true); 16 17 System.debug(expr.evaluate()); 18 //FALSE OR (TRUE AND TRUE) => TRUE
  • 47. 6 つのパターン 1.  Singleton 2.  Strategy 3.  sObject Decorater 4.  Façade 5.  Composite 6.  ?
  • 48. ビリーが新たな問題に直⾯面しています Opportunity (商談) レコードがクローズされたタイミングで Order (注⽂文) レコードを作成するトリガを記述したつもりだったが… §  クローズ済みの商談レコードが更更新されるたびに注⽂文レコードが作成されてしまう §  Apex データローダを使ってインポートを実⾏行行した場合に、 クローズ済みの商談レコードの⼀一部で注⽂文レコードが作成されないことがある
  • 49. 問題のコード 01 trigger OpptyTrigger on Opportunity (after insert, after update) { 02 03 if (trigger.new[0].isClosed) { 04 Order__c order = new Order__c(); 05 … 前の状態がどうであるかに 関係なく実⾏行行される 06 insert order 07 } バルク処理理に 08 } 対応していない 再利利⽤用性が低い
  • 50. こうしたらどうか? 01 trigger OpptyTrigger on Opportunity (after insert, after update) { 02 03 new OrderClass().CreateOrder(trigger.new); 04 05 } 01 public class OrderClass { 02 03 public void CreateOrder(List<Opportunity> opptyList) { 04 for (Opportunity oppty : opptyList) { 05 if (oppty.isClosed) { 06 Order__c order = new Order__c(); 07 ... 08 insert order; 前の状態がどうであるかに 関係なく実⾏行行される 09 } 10 } 11 12 } バルク処理理に 13 } 対応していない
  • 51. ではこうしたら? 01 trigger OpptyTrigger on Opportunity (before insert, before update) { 02 if (trigger.isInsert) { 03 new OrderClass().CreateOrder(trigger.newMap, null); 04 else 0506 new OrderClass().CreateOrder(trigger.newMap, trigger.oldMap); 01 public class OrderClass { 02 03 public void CreateOrder(Map<Opportunity> opptyMapNew 04 Map<Opportunity> opptyMapOld) { 05 List<Order__c> orderList = new List<Order__c>(); 06 for (Opportunity oppty : opptyMapNew.values()) { 07 if (oppty.isClosed && (opptyMapOld == null || 08 !opptyMapOld.get(oppty.id).isClosed)) { バルク処理理は 09 Order__c order = new Order__c(); OK  になった 10 ... 11 orderList.add(order); 12 } コードのコンテキスト依存度度が 13 } ⾼高く、再利利⽤用が難しい 14 insert orderList ; 15 }
  • 52. ソリューション – "Bulk State Transition" (バルク状態遷移) Utility Class Trigger + Foo(sObject[]) •  状態が変更更されたレコードの中で、 •  あらゆるコンテキストから 条件に⼀一致するものを検出 呼び出せるユーティリティクラスの •  所定の処理理を実⾏行行するユーティリティ 汎⽤用的なメソッド クラスのメソッドを呼び出す •  条件を満たすレコードのみを ユーティリティクラスに渡す
  • 53. これで問題解決!!! 01 trigger OpptyTrigger on Opportunity (after insert, after update) { 02 if (trigger.isAfter && (trigger.isInsert || trigger.isUpdate)) { 03 List<Opportunity> closedOpptyList = new List<Opportunity>(); 04 for (Opportunity oppty : trigger.new) { 05 if (oppty.isClosed && (trigger.isInsert || 06 (trigger.isUpdate && 07 !trigger.oldMap.get(oppty.id).isClosed))) トリガが状態遷移を 08 closedOpptyList.add(oppty); 適切切に処理理 09 } 10 if (!closedOpptyList.isEmpty()) 11 new OrderClass().CreateOrderFromOpptys(closedOpptyList) 01 public class OrderClass { 02 public void CreateOrdersFromOpptys(List<Opportunity> opptyList) { 03 List<Order__c> orderList = new List<Order__c>(); 04 for (Opportunity oppty : opptyList) { 05 Order__c order = new Order__c(); 06 ... 再利利⽤用性が⾼高まり、 07 orderList.add(order); バルク処理理にも対応 08 } 09 insert orderList ;
  • 54. 6 つのパターン 1.  Singleton 2.  Strategy 3.  sObject Decorater 4.  Façade 5.  Composite 6.  Bulk State Transition
  • 55. 関連リソース ラッパークラス http://wiki.developerforce.com/page/Wrapper_Class (英語) Apex コードに関するベストプラクティス http://wiki.developerforce.com/page/Apex_Code_Best_Practices (英語) Apex の Web サービスとコールアウト http://wiki.developerforce.com/page/JP:Apex_Web_Services_and_Callouts サンプルコード https://github.com/richardvanhook/Force.com-Patterns (英語)