Эта статья была написана мною в марте 2013 года. Оставляю ее без каких либо изменений.
Всем доброго времени суток. Вы знаете, накануне посмотрел замечательный фильм Дэвида Финчера "Семь". Да-да, тот самый про семь грехов. И буквально тут же словил себя на мысле, а какие "грехи" делают начинающие SalesForce developer'ы, ведь такого рода плохо написанного кода "преступления" затем приходится расследовать более опытным коллегам-сыщикам. Уподобившись маэстро Финчеру, на примере самого себя, сегодня я расскажу про несколько таких недопустимых ошибок. Статья исключительно для начинающих, вроде таких, как ваш покорный слуга (и должна быть дополнена!). Вы готовы "расскрыть парочку преступлений", чтобы в будущем не попадать в такие ловушки? Для начала рассмотрим, с чем я буду работать. ИмеемCRM-систему NBA Statistic Analyzer (вымышленная). Начинаем грешить и разбираться.
Всем доброго времени суток. Вы знаете, накануне посмотрел замечательный фильм Дэвида Финчера "Семь". Да-да, тот самый про семь грехов. И буквально тут же словил себя на мысле, а какие "грехи" делают начинающие SalesForce developer'ы, ведь такого рода плохо написанного кода "преступления" затем приходится расследовать более опытным коллегам-сыщикам. Уподобившись маэстро Финчеру, на примере самого себя, сегодня я расскажу про несколько таких недопустимых ошибок. Статья исключительно для начинающих, вроде таких, как ваш покорный слуга (и должна быть дополнена!). Вы готовы "расскрыть парочку преступлений", чтобы в будущем не попадать в такие ловушки? Для начала рассмотрим, с чем я буду работать. ИмеемCRM-систему NBA Statistic Analyzer (вымышленная). Начинаем грешить и разбираться.
Грех "Debug mode".
А вот и первая задачка (скорее даже "не преступление"), которая ставит в тупик любого любознательного salesforce-сыщика - это debug. То что предлагает платформа, на мой взгляд, назвать отличным , язык не позволяет (хотя и плохим тоже назвать нельзя!). Рады ли вы видеть в вашей консоле вот такую информацию?
А вот и первая задачка (скорее даже "не преступление"), которая ставит в тупик любого любознательного salesforce-сыщика - это debug. То что предлагает платформа, на мой взгляд, назвать отличным , язык не позволяет (хотя и плохим тоже назвать нельзя!). Рады ли вы видеть в вашей консоле вот такую информацию?
Выполнив всего лишь вот такой короткий простой кусок кода:
- public class MyTestPlayer{
- public static void helloPlayer(){
- Player__c player = new Player__c();
- player.College__c = 'Iowa';
- player.Experience__c = 8;
- player.First_Name__c = 'Arty';
- player.Last_Name__c = 'Leuchanka';
- System.debug(LoggingLevel.ERROR, 'You are add new Player NBA '+ player);
- }
- }
А что может случиться, если у нас десятки, сотни строчек кода, а нам, к примеру, нужно только посмотреть значения переменных. Есть! Панацея. Заходим в наш org. Далее аккуратненько следуем по этому пути (Setup->Monitoring->Debug Log). Выбираем Monitoring Users (Выбираем Filters). И убираем лишнее. Например, если нам нужно "продебажить значение переменной" в консоле, то выставляем нечто вроде таких параметров.
Или в Eclipse. Выполняем тот же метод и видим, что количество строчек существенно уменьшилось. А главное на пути просмотра значения переменной нет лишней информации.
Однако это не совсем спасает от того факта, что в Force-технологии нет возможности пошагового дебагинга. Поэтому отладчик держим в уме.
Грех "2 в 1 или объединяй и властвуй!"
При первом знакомстве SOQL-запросами и не подозреваешь, какие порой забавные и в тоже время не красивые ошибки мы допускаем. Есть одно золотое правило: чем меньше запросов к базе мы делаем, тем более эффективным оказывается наш код. Но не всегда мы об этом помним. Смотрим:
Player__c pl = [SELECT First_Name__c, Last_Name__c,Team__c from Player__c limit 1];
Team__c team = [SELECT Id, Number__c from Team__c whereId = :pl.Team__c limit 1];
В этом примере мы видим, что у нас два запроса, которые прекрасно работают. Но нам хочется более элегантного и, как мы условились из правила, более эффективного способа решения данной проблемы. Давайте объеденим эти два запроса в один.
Player__c pls = [SELECT First_Name__c, Last_Name__c, Team__r.Number__c from Player__c limit 1];
И теперь поле Number__c объекта Team__c нам доступно.
Грех "Операция LimitException 101-151! А также конструктор, как ты мне дорог!".
Умеете ли вы правильно пользоваться конструктором? А если хорошо подумать? Действительно в программировании на языке Apexнеобходимо быть предельно сконцентрированным и внимательным. Одним морозным утром я по обычаю уселся писать очередной (третий или четвертый на тот момент) свой триггер. Быстро покончив с этой простой задачей я уселся пить кофе и тестировать написанный код. Каково же было мое удивление, когда я получил вот такое сообщение:
System.LimitException: Too many SOQL queries: 101
Или вот такое:
System.LimitException: Too many DML statements: 151
Оказывается, за всеми моими действиями кто-то следит. Проштудировав пару форумов я понял, кто он! Apex governor следит за моими лимитами SOQL-запросов, DML statement'ами. Лимит есть лимит. Так что же делать тогда? У меня стояла довольно интересная и простая задача.
Грех "2 в 1 или объединяй и властвуй!"
При первом знакомстве SOQL-запросами и не подозреваешь, какие порой забавные и в тоже время не красивые ошибки мы допускаем. Есть одно золотое правило: чем меньше запросов к базе мы делаем, тем более эффективным оказывается наш код. Но не всегда мы об этом помним. Смотрим:
Player__c pl = [SELECT First_Name__c, Last_Name__c,Team__c from Player__c limit 1];
Team__c team = [SELECT Id, Number__c from Team__c whereId = :pl.Team__c limit 1];
В этом примере мы видим, что у нас два запроса, которые прекрасно работают. Но нам хочется более элегантного и, как мы условились из правила, более эффективного способа решения данной проблемы. Давайте объеденим эти два запроса в один.
Player__c pls = [SELECT First_Name__c, Last_Name__c, Team__r.Number__c from Player__c limit 1];
И теперь поле Number__c объекта Team__c нам доступно.
Грех "Операция LimitException 101-151! А также конструктор, как ты мне дорог!".
Умеете ли вы правильно пользоваться конструктором? А если хорошо подумать? Действительно в программировании на языке Apexнеобходимо быть предельно сконцентрированным и внимательным. Одним морозным утром я по обычаю уселся писать очередной (третий или четвертый на тот момент) свой триггер. Быстро покончив с этой простой задачей я уселся пить кофе и тестировать написанный код. Каково же было мое удивление, когда я получил вот такое сообщение:
System.LimitException: Too many SOQL queries: 101
Или вот такое:
System.LimitException: Too many DML statements: 151
Оказывается, за всеми моими действиями кто-то следит. Проштудировав пару форумов я понял, кто он! Apex governor следит за моими лимитами SOQL-запросов, DML statement'ами. Лимит есть лимит. Так что же делать тогда? У меня стояла довольно интересная и простая задача.
- public class MyTestPlayer{
- private String id{get; private set;}
- public MyTestPlayer(){
- // TODO:
- this.id = 'New String';
- }
- ...
- }
- trigger PlayerTrigger on Player__c (before insert, before update) {
- for(Player__c pl : trigger.new){
- MyTestPlayer player = new MyTestPlayer();
- ...
- // условно for
- ...
- insert object;
- ...
- }
- }
Мне нужно было перед insert-ом Player__c выполнить кое какое преобразование в Helper-классе MyTestPlayer. Также, каким-то невооброзимым образом у меня должно было вставиться в базу 120 записей, но этого не произошло. Дело оказалось в том, что я не мог выполнить 101 insert и у меня все покатилось валом! Более того, я заметил, что у меня 100 раз выделялась память под объекты MyTestPlayer (а это достаточно дорого). Оказывается в Apex жизненный цикл объектов проходит на протяжении транзакции, а не как в той же Java, в которой процессы протекают в JVM. Нагрешил так нагрешил! Будем исправлять. Для начала разберемся с конструктором. Зачем мне непонятная тучища объектов MyTestPlayer - достаточно и одного. И тут на помощь приходит паттерн Singleton.
- public class MyTestPlayer{
- private String id{get; private set;}
- private static MyTestPlayer instance = null;
- private MyTestPlayer(){
- // TODO:
- this.id = 'New String';
- ...
- }
- public static MyTestPlayer getInstance(){
- if(instance == null){
- instance = new MyTestPlayer();
- }
- return instance;
- }
- ...
- }
- trigger PlayerTrigger on Player__c (before insert, before update) {
- for(Player__c pl : trigger.new){
- MyTestPlayer player = MyTestPlayer.getInstance();
- ...
- }
- }
Теперь я могу не переживать за свой класс MyTestPlayer, гарантируя, что у класса будет только один экземпляр. Но как же обойти ограничения. Ведь мне нужно позарез занести в базу тучищу записей. Создаем в нашем горе триггере Set, которому передаем нашу коллекцию Trigger.new вместо сомнительной конструкции for:
- Set<Id> ids = Trigger.new.keySet();
- List<Player__c> players = [SELECT Id FROM Player__c WHERE Id in :ids];
- MyTestPlayer player = MyTestPlayer.getInstance();
Вот таким образом мы ограничили количество SOQL-запросов. Например, если бы у нас стояла задача еще что-то там вставить в базу, то операцию insert необходимо обернуть вот такой проверкой:
- List<Player__c> myTest = new List<Player__c>{};
- ...
- if((limits.getDmlRows() + myTest.size()) < limits.getDmlStatements()){
- insert myTest;
- }
Наверное, это одна из самых распространненых ошибок, допускаемая начинающими salesforce developer'ами. Но теперь мы предупреждены. Конечно, одним из хороших решений работы с таким количеством данных должен был стать Batch Apex. Но это уже совсем другая песня...
Грех: "Тестирование: create, invoke, check pattern of test development"
Начинающий developer, правильно ли ты пользуешься инструментом тестирования? Зачитываясь идей Чака Томанека (Chuck Tomanek), который является Senior Systems Analyst в компании Sundog, понимаю, что пользовался до этого времени не всем преимуществом Unit Test'ов в Apex. Итак, первое правило, которое нужно уяснить - это число 75. Именно 75% code coverage нам нужно достигнуть, чтобы наш Apex-код мог отправиться в production. Одним из хороших тонов, для написания теста, является использование связки test.startTest () и test.stopTest ().
Начинающий developer, правильно ли ты пользуешься инструментом тестирования? Зачитываясь идей Чака Томанека (Chuck Tomanek), который является Senior Systems Analyst в компании Sundog, понимаю, что пользовался до этого времени не всем преимуществом Unit Test'ов в Apex. Итак, первое правило, которое нужно уяснить - это число 75. Именно 75% code coverage нам нужно достигнуть, чтобы наш Apex-код мог отправиться в production. Одним из хороших тонов, для написания теста, является использование связки test.startTest () и test.stopTest ().
- @isTest
- private class MyTestClass {
- static testMethod void myTestOne(){
- // Create all your test data...
- test.startTest();
- // Invoike the code you are testing...
- test.stopTest();
- //perform your assertions to ensure your code behaves as expected...
- }
- }
Чак утверждает, что "Constructing your tests this way not only allows the code to run with the governor limits in place, it will be held to in real world execution, but it also is the only way to test asynchronous code. Invoking the asynchronous call after the startTest() method, and before the stopTest() method will allow all of the asynchronous code to complete before running the assertion statements and allows you to properly test your code from asynchronous methods. "
На платформе Force.com существует еще один лимит - это лимит на количество Apex кода. Хотя и цифра там внушительная и приблизиться к ней в плотную нам может и не удасться, но все-таки лучше сохранить свободное пространство. Для этого даже сам orgподсказывает сколько у нас осталось свободного места для написания кода, за исключением комментариев и кода помеченного аннотацией isTest . Поэтому тествовые классы будем помечать этой чудо аннотацией.
Список литературы:
http://www.sundoginteractive.com/whitepapers/SUN_Chuck_Apexdesignpatterns.pdf
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_patterns_bulk.htm
http://www.salesforce.com/us/developer/docs/apexcode/index.htm
http://www.salesforce.com/us/developer/docs/apexcode/Content/langCon_apex_SOQL_VLSQ.htm#EffSOQLSection
На платформе Force.com существует еще один лимит - это лимит на количество Apex кода. Хотя и цифра там внушительная и приблизиться к ней в плотную нам может и не удасться, но все-таки лучше сохранить свободное пространство. Для этого даже сам orgподсказывает сколько у нас осталось свободного места для написания кода, за исключением комментариев и кода помеченного аннотацией isTest . Поэтому тествовые классы будем помечать этой чудо аннотацией.
Список литературы:
http://www.sundoginteractive.com/whitepapers/SUN_Chuck_Apexdesignpatterns.pdf
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_patterns_bulk.htm
http://www.salesforce.com/us/developer/docs/apexcode/index.htm
http://www.salesforce.com/us/developer/docs/apexcode/Content/langCon_apex_SOQL_VLSQ.htm#EffSOQLSection