SlideShare ist ein Scribd-Unternehmen logo
1 von 57
Downloaden Sie, um offline zu lesen
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デザインパターン

Weitere ähnliche Inhalte

Was ist angesagt?

世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean ArchitectureAtsushi Nakamura
 
Spring bootでweb バリデート編
Spring bootでweb バリデート編Spring bootでweb バリデート編
Spring bootでweb バリデート編なべ
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)Yoshitaka Kawashima
 
ADRという考えを取り入れてみて
ADRという考えを取り入れてみてADRという考えを取り入れてみて
ADRという考えを取り入れてみてinfinite_loop
 
強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話Yoshitaka Kawashima
 
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門増田 亨
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門増田 亨
 
Junitを使ったjavaのテスト入門
Junitを使ったjavaのテスト入門Junitを使ったjavaのテスト入門
Junitを使ったjavaのテスト入門Satoshi Kubo
 
ちいさなオブジェクトでドメインモデルを組み立てる
ちいさなオブジェクトでドメインモデルを組み立てるちいさなオブジェクトでドメインモデルを組み立てる
ちいさなオブジェクトでドメインモデルを組み立てる増田 亨
 
今から始めようMicrosoft PowerApps! (2017年版) - 吉田の備忘録
今から始めようMicrosoft PowerApps! (2017年版) - 吉田の備忘録今から始めようMicrosoft PowerApps! (2017年版) - 吉田の備忘録
今から始めようMicrosoft PowerApps! (2017年版) - 吉田の備忘録Taiki Yoshida
 
PHPからgoへの移行で分かったこと
PHPからgoへの移行で分かったことPHPからgoへの移行で分かったこと
PHPからgoへの移行で分かったことgree_tech
 
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門Tadahiro Ishisaka
 
ビジネスルールの複雑さに立ち向かう
ビジネスルールの複雑さに立ち向かうビジネスルールの複雑さに立ち向かう
ビジネスルールの複雑さに立ち向かう増田 亨
 
AI時代の要件定義
AI時代の要件定義AI時代の要件定義
AI時代の要件定義Zenji Kanzaki
 
マルチテナントのアプリケーション実装〜実践編〜
マルチテナントのアプリケーション実装〜実践編〜マルチテナントのアプリケーション実装〜実践編〜
マルチテナントのアプリケーション実装〜実践編〜Yoshiki Nakagawa
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugMasatoshi Tada
 
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)Mikiya Okuno
 
ドメイン駆動設計の正しい歩き方
ドメイン駆動設計の正しい歩き方ドメイン駆動設計の正しい歩き方
ドメイン駆動設計の正しい歩き方増田 亨
 
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかDDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかKoichiro Matsuoka
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!mosa siru
 

Was ist angesagt? (20)

世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
Spring bootでweb バリデート編
Spring bootでweb バリデート編Spring bootでweb バリデート編
Spring bootでweb バリデート編
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)
 
ADRという考えを取り入れてみて
ADRという考えを取り入れてみてADRという考えを取り入れてみて
ADRという考えを取り入れてみて
 
強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話強いて言えば「集約どう実装するのかな、を考える」な話
強いて言えば「集約どう実装するのかな、を考える」な話
 
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門
 
Junitを使ったjavaのテスト入門
Junitを使ったjavaのテスト入門Junitを使ったjavaのテスト入門
Junitを使ったjavaのテスト入門
 
ちいさなオブジェクトでドメインモデルを組み立てる
ちいさなオブジェクトでドメインモデルを組み立てるちいさなオブジェクトでドメインモデルを組み立てる
ちいさなオブジェクトでドメインモデルを組み立てる
 
今から始めようMicrosoft PowerApps! (2017年版) - 吉田の備忘録
今から始めようMicrosoft PowerApps! (2017年版) - 吉田の備忘録今から始めようMicrosoft PowerApps! (2017年版) - 吉田の備忘録
今から始めようMicrosoft PowerApps! (2017年版) - 吉田の備忘録
 
PHPからgoへの移行で分かったこと
PHPからgoへの移行で分かったことPHPからgoへの移行で分かったこと
PHPからgoへの移行で分かったこと
 
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門
 
ビジネスルールの複雑さに立ち向かう
ビジネスルールの複雑さに立ち向かうビジネスルールの複雑さに立ち向かう
ビジネスルールの複雑さに立ち向かう
 
AI時代の要件定義
AI時代の要件定義AI時代の要件定義
AI時代の要件定義
 
マルチテナントのアプリケーション実装〜実践編〜
マルチテナントのアプリケーション実装〜実践編〜マルチテナントのアプリケーション実装〜実践編〜
マルチテナントのアプリケーション実装〜実践編〜
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
 
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
 
ドメイン駆動設計の正しい歩き方
ドメイン駆動設計の正しい歩き方ドメイン駆動設計の正しい歩き方
ドメイン駆動設計の正しい歩き方
 
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかDDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 

Andere mochten auch

インターンシップ成果報告会 発表資料
インターンシップ成果報告会 発表資料インターンシップ成果報告会 発表資料
インターンシップ成果報告会 発表資料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
 

Andere mochten auch (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
 

Ähnlich wie 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
 
ApexトリガのBest Practiceを目指して
ApexトリガのBest Practiceを目指してApexトリガのBest Practiceを目指して
ApexトリガのBest Practiceを目指してTakahiro Yonei
 
エンタープライズ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
 

Ähnlich wie 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
 
ApexトリガのBest Practiceを目指して
ApexトリガのBest Practiceを目指してApexトリガのBest Practiceを目指して
ApexトリガのBest Practiceを目指して
 
エンタープライズ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
 

Mehr von Salesforce Developers Japan

Salesforce DX の始め方とパートナー様成功事例
Salesforce DX の始め方とパートナー様成功事例Salesforce DX の始め方とパートナー様成功事例
Salesforce DX の始め方とパートナー様成功事例Salesforce Developers Japan
 
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみようデータ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみようSalesforce Developers Japan
 
Einstein Analyticsでのデータ取り込みと加工
Einstein Analyticsでのデータ取り込みと加工Einstein Analyticsでのデータ取り込みと加工
Einstein Analyticsでのデータ取り込みと加工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
 

Mehr von Salesforce Developers Japan (20)

Salesforce DX の始め方とパートナー様成功事例
Salesforce DX の始め方とパートナー様成功事例Salesforce DX の始め方とパートナー様成功事例
Salesforce DX の始め方とパートナー様成功事例
 
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみようデータ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
データ連携の新しいカタチ - 変更データキャプチャ/プラットフォームイベントを MuleSoft Anypoint Platform と組み合わせて試してみよう
 
Einstein Analyticsでのデータ取り込みと加工
Einstein Analyticsでのデータ取り込みと加工Einstein Analyticsでのデータ取り込みと加工
Einstein Analyticsでのデータ取り込みと加工
 
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!
 

Kürzlich hochgeladen

知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptxsn679259
 
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Gamesatsushi061452
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。iPride Co., Ltd.
 
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video UnderstandingToru Tamaki
 
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Hiroshi Tomioka
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。iPride Co., Ltd.
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイスCRI Japan, Inc.
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルCRI Japan, Inc.
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。iPride Co., Ltd.
 
Utilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native IntegrationsUtilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native IntegrationsWSO2
 
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...Toru Tamaki
 

Kürzlich hochgeladen (11)

知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
 
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
 
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
Utilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native IntegrationsUtilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native Integrations
 
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
 

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