SlideShare une entreprise Scribd logo
1  sur  57
Télécharger pour lire hors ligne
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デザインパターン

Contenu connexe

Tendances

Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発Yahoo!デベロッパーネットワーク
 
Linuxの2038年問題を調べてみた
Linuxの2038年問題を調べてみたLinuxの2038年問題を調べてみた
Linuxの2038年問題を調べてみたwata2ki
 
Salesforce integration best practices columbus meetup
Salesforce integration best practices   columbus meetupSalesforce integration best practices   columbus meetup
Salesforce integration best practices columbus meetupMuleSoft Meetup
 
ツイートID生成とツイッターリアルタイム検索システムの話
ツイートID生成とツイッターリアルタイム検索システムの話ツイートID生成とツイッターリアルタイム検索システムの話
ツイートID生成とツイッターリアルタイム検索システムの話Preferred Networks
 
アプリケーションライフサイクル管理とロック解除済みパッケージ
アプリケーションライフサイクル管理とロック解除済みパッケージアプリケーションライフサイクル管理とロック解除済みパッケージ
アプリケーションライフサイクル管理とロック解除済みパッケージTakahiro Kawabata
 
Clojureに入門してみた、2017年秋
Clojureに入門してみた、2017年秋Clojureに入門してみた、2017年秋
Clojureに入門してみた、2017年秋Satoshi KOBAYASHI
 
Haskell Day2012 - 参照透過性とは何だったのか
Haskell Day2012 - 参照透過性とは何だったのかHaskell Day2012 - 参照透過性とは何だったのか
Haskell Day2012 - 参照透過性とは何だったのかKousuke Ruichi
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション土岐 孝平
 
Einstein Analyticsでのデータ取り込みと加工
Einstein Analyticsでのデータ取り込みと加工Einstein Analyticsでのデータ取り込みと加工
Einstein Analyticsでのデータ取り込みと加工Salesforce Developers Japan
 
The Art of Clean code
The Art of Clean codeThe Art of Clean code
The Art of Clean codeVictor Rentea
 
これからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきことこれからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきこと土岐 孝平
 
"Simple Made Easy" Made Easy
"Simple Made Easy" Made Easy"Simple Made Easy" Made Easy
"Simple Made Easy" Made EasyKent Ohashi
 
「DNS浸透いうな」と言うけれど… (#ssmjp 2018/07)
「DNS浸透いうな」と言うけれど… (#ssmjp 2018/07)「DNS浸透いうな」と言うけれど… (#ssmjp 2018/07)
「DNS浸透いうな」と言うけれど… (#ssmjp 2018/07)Yoshikazu GOTO
 
Successfully creating unlocked package
Successfully creating unlocked packageSuccessfully creating unlocked package
Successfully creating unlocked packageMohith Shrivastava
 
日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについてkumake
 
Salesforceの標準オブジェクトについて復習してみた
Salesforceの標準オブジェクトについて復習してみたSalesforceの標準オブジェクトについて復習してみた
Salesforceの標準オブジェクトについて復習してみたy-maeda
 

Tendances (20)

Visualforceを使ってみよう
Visualforceを使ってみようVisualforceを使ってみよう
Visualforceを使ってみよう
 
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
 
Linuxの2038年問題を調べてみた
Linuxの2038年問題を調べてみたLinuxの2038年問題を調べてみた
Linuxの2038年問題を調べてみた
 
Salesforce integration best practices columbus meetup
Salesforce integration best practices   columbus meetupSalesforce integration best practices   columbus meetup
Salesforce integration best practices columbus meetup
 
ツイートID生成とツイッターリアルタイム検索システムの話
ツイートID生成とツイッターリアルタイム検索システムの話ツイートID生成とツイッターリアルタイム検索システムの話
ツイートID生成とツイッターリアルタイム検索システムの話
 
アプリケーションライフサイクル管理とロック解除済みパッケージ
アプリケーションライフサイクル管理とロック解除済みパッケージアプリケーションライフサイクル管理とロック解除済みパッケージ
アプリケーションライフサイクル管理とロック解除済みパッケージ
 
Clojureに入門してみた、2017年秋
Clojureに入門してみた、2017年秋Clojureに入門してみた、2017年秋
Clojureに入門してみた、2017年秋
 
Haskell Day2012 - 参照透過性とは何だったのか
Haskell Day2012 - 参照透過性とは何だったのかHaskell Day2012 - 参照透過性とは何だったのか
Haskell Day2012 - 参照透過性とは何だったのか
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
 
Einstein Analyticsでのデータ取り込みと加工
Einstein Analyticsでのデータ取り込みと加工Einstein Analyticsでのデータ取り込みと加工
Einstein Analyticsでのデータ取り込みと加工
 
Goとテスト
GoとテストGoとテスト
Goとテスト
 
Apex Design Patterns
Apex Design PatternsApex Design Patterns
Apex Design Patterns
 
The Art of Clean code
The Art of Clean codeThe Art of Clean code
The Art of Clean code
 
これからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきことこれからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきこと
 
"Simple Made Easy" Made Easy
"Simple Made Easy" Made Easy"Simple Made Easy" Made Easy
"Simple Made Easy" Made Easy
 
「DNS浸透いうな」と言うけれど… (#ssmjp 2018/07)
「DNS浸透いうな」と言うけれど… (#ssmjp 2018/07)「DNS浸透いうな」と言うけれど… (#ssmjp 2018/07)
「DNS浸透いうな」と言うけれど… (#ssmjp 2018/07)
 
Successfully creating unlocked package
Successfully creating unlocked packageSuccessfully creating unlocked package
Successfully creating unlocked package
 
日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについて
 
Salesforceの標準オブジェクトについて復習してみた
Salesforceの標準オブジェクトについて復習してみたSalesforceの標準オブジェクトについて復習してみた
Salesforceの標準オブジェクトについて復習してみた
 
Power Apps + C#
Power Apps + C#Power Apps + C#
Power Apps + C#
 

En vedette

インターンシップ成果報告会 発表資料
インターンシップ成果報告会 発表資料インターンシップ成果報告会 発表資料
インターンシップ成果報告会 発表資料T2C_
 
Spring'16 Apex Code 新機能
Spring'16 Apex Code 新機能Spring'16 Apex Code 新機能
Spring'16 Apex Code 新機能Taiki Yoshikawa
 
Spring'17リリースノート輪読会 API By フレクト
Spring'17リリースノート輪読会 API By フレクトSpring'17リリースノート輪読会 API By フレクト
Spring'17リリースノート輪読会 API By フレクト政雄 金森
 
tokyo-salesforce-dg-meetup-2017-etl-with-sphinx
tokyo-salesforce-dg-meetup-2017-etl-with-sphinxtokyo-salesforce-dg-meetup-2017-etl-with-sphinx
tokyo-salesforce-dg-meetup-2017-etl-with-sphinxshun saito
 
Sf素人が2週間でアプリケーションビルダーに挑戦してみた
Sf素人が2週間でアプリケーションビルダーに挑戦してみたSf素人が2週間でアプリケーションビルダーに挑戦してみた
Sf素人が2週間でアプリケーションビルダーに挑戦してみた政雄 金森
 
Salesforce Development Best Practices
Salesforce Development Best PracticesSalesforce Development Best Practices
Salesforce Development Best PracticesVivek Chawla
 
Salesforceの導入で押さえておきたいポイント
Salesforceの導入で押さえておきたいポイントSalesforceの導入で押さえておきたいポイント
Salesforceの導入で押さえておきたいポイントTaiki Yoshikawa
 
Customizing the salesforce console with the integration toolkit mt
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 mtSalesforce Developers
 

En vedette (8)

インターンシップ成果報告会 発表資料
インターンシップ成果報告会 発表資料インターンシップ成果報告会 発表資料
インターンシップ成果報告会 発表資料
 
Spring'16 Apex Code 新機能
Spring'16 Apex Code 新機能Spring'16 Apex Code 新機能
Spring'16 Apex Code 新機能
 
Spring'17リリースノート輪読会 API By フレクト
Spring'17リリースノート輪読会 API By フレクトSpring'17リリースノート輪読会 API By フレクト
Spring'17リリースノート輪読会 API By フレクト
 
tokyo-salesforce-dg-meetup-2017-etl-with-sphinx
tokyo-salesforce-dg-meetup-2017-etl-with-sphinxtokyo-salesforce-dg-meetup-2017-etl-with-sphinx
tokyo-salesforce-dg-meetup-2017-etl-with-sphinx
 
Sf素人が2週間でアプリケーションビルダーに挑戦してみた
Sf素人が2週間でアプリケーションビルダーに挑戦してみたSf素人が2週間でアプリケーションビルダーに挑戦してみた
Sf素人が2週間でアプリケーションビルダーに挑戦してみた
 
Salesforce Development Best Practices
Salesforce Development Best PracticesSalesforce Development Best Practices
Salesforce Development Best Practices
 
Salesforceの導入で押さえておきたいポイント
Salesforceの導入で押さえておきたいポイントSalesforceの導入で押さえておきたいポイント
Salesforceの導入で押さえておきたいポイント
 
Customizing the salesforce console with the integration toolkit mt
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
 

Similaire à Apexデザインパターン

Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」までNeo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」までKeiichiro Seida
 
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)Akira Kuratani
 
Tech talk salesforce mobile sdk
Tech talk   salesforce mobile sdkTech talk   salesforce mobile sdk
Tech talk salesforce mobile sdkKazuki Nakajima
 
システムのモダナイズ 落ちても良いアプリの作り方
システムのモダナイズ 落ちても良いアプリの作り方システムのモダナイズ 落ちても良いアプリの作り方
システムのモダナイズ 落ちても良いアプリの作り方Chihiro Ito
 
Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理土岐 孝平
 
Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用stomita
 
勉強会force#3 iOSアプリ開発
勉強会force#3 iOSアプリ開発勉強会force#3 iOSアプリ開発
勉強会force#3 iOSアプリ開発Kazuki Nakajima
 
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014Takashi Yahata
 
Data api workshop at Co-Edo
Data api workshop at Co-EdoData api workshop at Co-Edo
Data api workshop at Co-EdoYuji Takayama
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回Naoyuki Yamada
 
Entity Framework 5.0 deep dive
Entity Framework 5.0 deep diveEntity Framework 5.0 deep dive
Entity Framework 5.0 deep diveAtsushi Fukui
 
JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」
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 Ohnaka
 
初めての Data API CMS どうでしょう - 仙台編 -
初めての Data API   CMS どうでしょう - 仙台編 -初めての Data API   CMS どうでしょう - 仙台編 -
初めての Data API CMS どうでしょう - 仙台編 -Yuji Takayama
 
AppExchangeパートナー&デベロッパー第2部:20070523版
AppExchangeパートナー&デベロッパー第2部:20070523版AppExchangeパートナー&デベロッパー第2部:20070523版
AppExchangeパートナー&デベロッパー第2部:20070523版Junichiro Tasaki
 
ドメイン駆動設計(DDD)の実践Part2
ドメイン駆動設計(DDD)の実践Part2ドメイン駆動設計(DDD)の実践Part2
ドメイン駆動設計(DDD)の実践Part2増田 亨
 
はじめてのCodeIgniter
はじめてのCodeIgniterはじめてのCodeIgniter
はじめてのCodeIgniterYuya Matsushima
 
CodeIgniterによるPhwittr
CodeIgniterによるPhwittrCodeIgniterによるPhwittr
CodeIgniterによるPhwittrkenjis
 

Similaire à Apexデザインパターン (20)

Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」までNeo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
 
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)
夏サミ2012 [A-2]ソーシャルプラットフォームを使った業務アプリ開発の現場(ソーシャル機能を取り入れたエンタープライズアプリケーション)
 
Tech talk salesforce mobile sdk
Tech talk   salesforce mobile sdkTech talk   salesforce mobile sdk
Tech talk salesforce mobile sdk
 
システムのモダナイズ 落ちても良いアプリの作り方
システムのモダナイズ 落ちても良いアプリの作り方システムのモダナイズ 落ちても良いアプリの作り方
システムのモダナイズ 落ちても良いアプリの作り方
 
Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理
 
Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用Apexコアデベロッパーセミナー070726 配布用
Apexコアデベロッパーセミナー070726 配布用
 
勉強会force#3 iOSアプリ開発
勉強会force#3 iOSアプリ開発勉強会force#3 iOSアプリ開発
勉強会force#3 iOSアプリ開発
 
CSS2020 Client Policies on keycloak
CSS2020 Client Policies on keycloak CSS2020 Client Policies on keycloak
CSS2020 Client Policies on keycloak
 
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
 
★2章
★2章★2章
★2章
 
Data api workshop at Co-Edo
Data api workshop at Co-EdoData api workshop at Co-Edo
Data api workshop at Co-Edo
 
C#6.0の新機能紹介
C#6.0の新機能紹介C#6.0の新機能紹介
C#6.0の新機能紹介
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
 
Entity Framework 5.0 deep dive
Entity Framework 5.0 deep diveEntity Framework 5.0 deep dive
Entity Framework 5.0 deep dive
 
JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」
JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」
JavaOne 2015 報告会 @ 東京 「About MVC 1.0 & JSON-P」
 
初めての Data API CMS どうでしょう - 仙台編 -
初めての Data API   CMS どうでしょう - 仙台編 -初めての Data API   CMS どうでしょう - 仙台編 -
初めての Data API CMS どうでしょう - 仙台編 -
 
AppExchangeパートナー&デベロッパー第2部:20070523版
AppExchangeパートナー&デベロッパー第2部:20070523版AppExchangeパートナー&デベロッパー第2部:20070523版
AppExchangeパートナー&デベロッパー第2部:20070523版
 
ドメイン駆動設計(DDD)の実践Part2
ドメイン駆動設計(DDD)の実践Part2ドメイン駆動設計(DDD)の実践Part2
ドメイン駆動設計(DDD)の実践Part2
 
はじめてのCodeIgniter
はじめてのCodeIgniterはじめてのCodeIgniter
はじめてのCodeIgniter
 
CodeIgniterによるPhwittr
CodeIgniterによるPhwittrCodeIgniterによるPhwittr
CodeIgniterによるPhwittr
 

Plus de Salesforce Developers Japan

データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみようデータ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみようSalesforce Developers Japan
 
GMOペパボのエンジニアが語るHeroku活用ノウハウ
GMOペパボのエンジニアが語るHeroku活用ノウハウGMOペパボのエンジニアが語るHeroku活用ノウハウ
GMOペパボのエンジニアが語るHeroku活用ノウハウSalesforce Developers Japan
 
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜Salesforce Developers Japan
 
Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発
Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発
Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発Salesforce Developers Japan
 
Lightning時代のService Cloud概要とカスタマイズ
Lightning時代のService Cloud概要とカスタマイズLightning時代のService Cloud概要とカスタマイズ
Lightning時代のService Cloud概要とカスタマイズSalesforce Developers Japan
 
Spring '19リリース開発者向け新機能セミナー
Spring '19リリース開発者向け新機能セミナーSpring '19リリース開発者向け新機能セミナー
Spring '19リリース開発者向け新機能セミナーSalesforce Developers Japan
 
業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -
業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -
業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -Salesforce Developers Japan
 
MuleSoft Anypoint Platformのコンセプトとサービス
MuleSoft Anypoint PlatformのコンセプトとサービスMuleSoft Anypoint Platformのコンセプトとサービス
MuleSoft Anypoint PlatformのコンセプトとサービスSalesforce Developers Japan
 
IoTで成功を収めるための製品と戦略 〜 Salesforce IoT 〜
IoTで成功を収めるための製品と戦略 〜 Salesforce IoT 〜IoTで成功を収めるための製品と戦略 〜 Salesforce IoT 〜
IoTで成功を収めるための製品と戦略 〜 Salesforce IoT 〜Salesforce Developers Japan
 
Lightning時代のレポート ダッシュボード & Flow 最前線
Lightning時代のレポート ダッシュボード & Flow 最前線Lightning時代のレポート ダッシュボード & Flow 最前線
Lightning時代のレポート ダッシュボード & Flow 最前線Salesforce Developers Japan
 
Summer18 開発者向け新機能Webセミナー
Summer18 開発者向け新機能WebセミナーSummer18 開発者向け新機能Webセミナー
Summer18 開発者向け新機能WebセミナーSalesforce Developers Japan
 
AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方
AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方
AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方Salesforce Developers Japan
 

Plus de Salesforce Developers Japan (20)

データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみようデータ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
 
GMOペパボのエンジニアが語るHeroku活用ノウハウ
GMOペパボのエンジニアが語るHeroku活用ノウハウGMOペパボのエンジニアが語るHeroku活用ノウハウ
GMOペパボのエンジニアが語るHeroku活用ノウハウ
 
Salesforce Big Object 最前線
Salesforce Big Object 最前線Salesforce Big Object 最前線
Salesforce Big Object 最前線
 
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜
Salesforce 開発者向け最新情報 Web セミナー 〜 TrailheaDX での新発表 & Summer '19 リリース新機能 〜
 
Einstein Next Best Action を試してみよう
Einstein Next Best Action を試してみようEinstein Next Best Action を試してみよう
Einstein Next Best Action を試してみよう
 
Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発
Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発
Salesforce DXとLightning Web ComponentsでモダンSalesforceアプリ開発
 
Lightning時代のService Cloud概要とカスタマイズ
Lightning時代のService Cloud概要とカスタマイズLightning時代のService Cloud概要とカスタマイズ
Lightning時代のService Cloud概要とカスタマイズ
 
Spring '19リリース開発者向け新機能セミナー
Spring '19リリース開発者向け新機能セミナーSpring '19リリース開発者向け新機能セミナー
Spring '19リリース開発者向け新機能セミナー
 
業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -
業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -
業務課題の解決に、データ分析・予測結果の活用を - Einstein Discovery / Einstein 予測ビルダーのご紹介 -
 
Einstein analyticsdashboardwebinar
Einstein analyticsdashboardwebinarEinstein analyticsdashboardwebinar
Einstein analyticsdashboardwebinar
 
MuleSoft Anypoint Platformのコンセプトとサービス
MuleSoft Anypoint PlatformのコンセプトとサービスMuleSoft Anypoint Platformのコンセプトとサービス
MuleSoft Anypoint Platformのコンセプトとサービス
 
IoTで成功を収めるための製品と戦略 〜 Salesforce IoT 〜
IoTで成功を収めるための製品と戦略 〜 Salesforce IoT 〜IoTで成功を収めるための製品と戦略 〜 Salesforce IoT 〜
IoTで成功を収めるための製品と戦略 〜 Salesforce IoT 〜
 
Heroku seminar winter19
Heroku seminar winter19Heroku seminar winter19
Heroku seminar winter19
 
Dreamforce18 update platform
Dreamforce18 update platformDreamforce18 update platform
Dreamforce18 update platform
 
Winter '19 開発者向け新機能
Winter '19 開発者向け新機能Winter '19 開発者向け新機能
Winter '19 開発者向け新機能
 
Lightning時代のレポート ダッシュボード & Flow 最前線
Lightning時代のレポート ダッシュボード & Flow 最前線Lightning時代のレポート ダッシュボード & Flow 最前線
Lightning時代のレポート ダッシュボード & Flow 最前線
 
Summer18 開発者向け新機能Webセミナー
Summer18 開発者向け新機能WebセミナーSummer18 開発者向け新機能Webセミナー
Summer18 開発者向け新機能Webセミナー
 
使ってみよう、Salesforce Big Object!
使ってみよう、Salesforce Big Object!使ってみよう、Salesforce Big Object!
使ってみよう、Salesforce Big Object!
 
AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方
AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方
AIアプリはこう作る!-独自の識別モデル作成も簡単 Einstein Platform Services の使い方
 
Spring '18 開発者向け新機能
Spring '18 開発者向け新機能Spring '18 開発者向け新機能
Spring '18 開発者向け新機能
 

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 (英語)