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?

Cloud runのオートスケールを検証してみる
Cloud runのオートスケールを検証してみるCloud runのオートスケールを検証してみる
Cloud runのオートスケールを検証してみる虎の穴 開発室
 
はじめようLightningコンポーネント
はじめようLightningコンポーネントはじめようLightningコンポーネント
はじめようLightningコンポーネントSalesforce Developers Japan
 
[AC05] マイクロサービスは分割がキモ!基幹システムのためのドメイン駆動設計
[AC05] マイクロサービスは分割がキモ!基幹システムのためのドメイン駆動設計[AC05] マイクロサービスは分割がキモ!基幹システムのためのドメイン駆動設計
[AC05] マイクロサービスは分割がキモ!基幹システムのためのドメイン駆動設計de:code 2017
 
Salesforceの標準オブジェクトについて復習してみた
Salesforceの標準オブジェクトについて復習してみたSalesforceの標準オブジェクトについて復習してみた
Salesforceの標準オブジェクトについて復習してみたy-maeda
 
エスイーが要件定義でやるべきたったひとつのこと
エスイーが要件定義でやるべきたったひとつのことエスイーが要件定義でやるべきたったひとつのこと
エスイーが要件定義でやるべきたったひとつのことYoshitaka Kawashima
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装infinite_loop
 
Application Re-Architecture Technology ~ StrutsからSpring MVCへ ~
Application Re-Architecture Technology ~ StrutsからSpring MVCへ ~Application Re-Architecture Technology ~ StrutsからSpring MVCへ ~
Application Re-Architecture Technology ~ StrutsからSpring MVCへ ~Yuichi Hasegawa
 
新標準PSRに学ぶきれいなPHP
新標準PSRに学ぶきれいなPHP新標準PSRに学ぶきれいなPHP
新標準PSRに学ぶきれいなPHPYusuke Ando
 
RDFチェックツール「rdflint」のご紹介
RDFチェックツール「rdflint」のご紹介RDFチェックツール「rdflint」のご紹介
RDFチェックツール「rdflint」のご紹介Takeshi Mikami
 
DX 時代の新たなソフトウェア工学に向けて: SWEBOK と SE4BS の挑戦
DX 時代の新たなソフトウェア工学に向けて: SWEBOK と SE4BS の挑戦DX 時代の新たなソフトウェア工学に向けて: SWEBOK と SE4BS の挑戦
DX 時代の新たなソフトウェア工学に向けて: SWEBOK と SE4BS の挑戦Hironori Washizaki
 
認定テクニカルアーキテクト取ろうぜ
認定テクニカルアーキテクト取ろうぜ認定テクニカルアーキテクト取ろうぜ
認定テクニカルアーキテクト取ろうぜHiroki Sato
 
Power BI のいろいろな活用パターン
Power BI のいろいろな活用パターンPower BI のいろいろな活用パターン
Power BI のいろいろな活用パターンYugo Shimizu
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?Yoshitaka Kawashima
 
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしようPHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしようShohei Okada
 
A comprehensive guide to Salesforce Org Strategy
A comprehensive guide to Salesforce Org StrategyA comprehensive guide to Salesforce Org Strategy
A comprehensive guide to Salesforce Org StrategyGaytri khandelwal
 
基礎からのOAuth 2.0とSpring Security 5.1による実装
基礎からのOAuth 2.0とSpring Security 5.1による実装基礎からのOAuth 2.0とSpring Security 5.1による実装
基礎からのOAuth 2.0とSpring Security 5.1による実装Masatoshi Tada
 
SAP Extractorのソースエンドポイントとしての利用
SAP Extractorのソースエンドポイントとしての利用SAP Extractorのソースエンドポイントとしての利用
SAP Extractorのソースエンドポイントとしての利用QlikPresalesJapan
 
データベース13 - トランザクションと障害回復
データベース13 - トランザクションと障害回復データベース13 - トランザクションと障害回復
データベース13 - トランザクションと障害回復Kenta Oku
 

Was ist angesagt? (20)

Cloud runのオートスケールを検証してみる
Cloud runのオートスケールを検証してみるCloud runのオートスケールを検証してみる
Cloud runのオートスケールを検証してみる
 
はじめようLightningコンポーネント
はじめようLightningコンポーネントはじめようLightningコンポーネント
はじめようLightningコンポーネント
 
[AC05] マイクロサービスは分割がキモ!基幹システムのためのドメイン駆動設計
[AC05] マイクロサービスは分割がキモ!基幹システムのためのドメイン駆動設計[AC05] マイクロサービスは分割がキモ!基幹システムのためのドメイン駆動設計
[AC05] マイクロサービスは分割がキモ!基幹システムのためのドメイン駆動設計
 
Salesforceの標準オブジェクトについて復習してみた
Salesforceの標準オブジェクトについて復習してみたSalesforceの標準オブジェクトについて復習してみた
Salesforceの標準オブジェクトについて復習してみた
 
pg_bigmを用いた全文検索のしくみ(後編)
pg_bigmを用いた全文検索のしくみ(後編)pg_bigmを用いた全文検索のしくみ(後編)
pg_bigmを用いた全文検索のしくみ(後編)
 
エスイーが要件定義でやるべきたったひとつのこと
エスイーが要件定義でやるべきたったひとつのことエスイーが要件定義でやるべきたったひとつのこと
エスイーが要件定義でやるべきたったひとつのこと
 
ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装ソーシャルゲーム案件におけるDB分割のPHP実装
ソーシャルゲーム案件におけるDB分割のPHP実装
 
Application Re-Architecture Technology ~ StrutsからSpring MVCへ ~
Application Re-Architecture Technology ~ StrutsからSpring MVCへ ~Application Re-Architecture Technology ~ StrutsからSpring MVCへ ~
Application Re-Architecture Technology ~ StrutsからSpring MVCへ ~
 
新標準PSRに学ぶきれいなPHP
新標準PSRに学ぶきれいなPHP新標準PSRに学ぶきれいなPHP
新標準PSRに学ぶきれいなPHP
 
RDFチェックツール「rdflint」のご紹介
RDFチェックツール「rdflint」のご紹介RDFチェックツール「rdflint」のご紹介
RDFチェックツール「rdflint」のご紹介
 
DX 時代の新たなソフトウェア工学に向けて: SWEBOK と SE4BS の挑戦
DX 時代の新たなソフトウェア工学に向けて: SWEBOK と SE4BS の挑戦DX 時代の新たなソフトウェア工学に向けて: SWEBOK と SE4BS の挑戦
DX 時代の新たなソフトウェア工学に向けて: SWEBOK と SE4BS の挑戦
 
認定テクニカルアーキテクト取ろうぜ
認定テクニカルアーキテクト取ろうぜ認定テクニカルアーキテクト取ろうぜ
認定テクニカルアーキテクト取ろうぜ
 
Power BI のいろいろな活用パターン
Power BI のいろいろな活用パターンPower BI のいろいろな活用パターン
Power BI のいろいろな活用パターン
 
噛み砕いてKafka Streams #kafkajp
噛み砕いてKafka Streams #kafkajp噛み砕いてKafka Streams #kafkajp
噛み砕いてKafka Streams #kafkajp
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?
 
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしようPHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
 
A comprehensive guide to Salesforce Org Strategy
A comprehensive guide to Salesforce Org StrategyA comprehensive guide to Salesforce Org Strategy
A comprehensive guide to Salesforce Org Strategy
 
基礎からのOAuth 2.0とSpring Security 5.1による実装
基礎からのOAuth 2.0とSpring Security 5.1による実装基礎からのOAuth 2.0とSpring Security 5.1による実装
基礎からのOAuth 2.0とSpring Security 5.1による実装
 
SAP Extractorのソースエンドポイントとしての利用
SAP Extractorのソースエンドポイントとしての利用SAP Extractorのソースエンドポイントとしての利用
SAP Extractorのソースエンドポイントとしての利用
 
データベース13 - トランザクションと障害回復
データベース13 - トランザクションと障害回復データベース13 - トランザクションと障害回復
データベース13 - トランザクションと障害回復
 

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

Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。iPride Co., Ltd.
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)Hiroshi Tomioka
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイスCRI Japan, Inc.
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。iPride Co., Ltd.
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルCRI Japan, Inc.
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NTT DATA Technology & Innovation
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。iPride Co., Ltd.
 

Kürzlich hochgeladen (7)

Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 

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