parser

Написать ответ на текущее сообщение

 

 
   команды управления поиском

Пару слов о свойствах...

Sumo 25.08.2007 12:20 / 25.08.2007 12:22

Начиная с версии 3.2 в Парсере появилась возможность определять свойства (properties). Кратко напомню: обращение к свойству выглядит в программе, как обычное обращение к переменным, но на самом деле происходит вызов промежуточных функций, которые могут содержать более сложную логику, чем простое чтение/запись переменной.

Замечательно, но в чем реальная польза от свойств? Вот несколько примеров.

Предположим, что у нас в БД есть таблица settings(name, value) в которой лежит некоторое количество переменных для нашей системы/сайта.
@CLASS
simpleModule

@create[]
  ...

@GET_siteName[]
  $result[^CSQL.string{select value from settings where name='site_name'}]

@SET_siteName[aSiteName]
  ^if(def $aSiteName){
    ^CSQL.void{replace settings (name, value) values ('site_name', '$aSiteName')}
  }

@run[]
  Название сайта: $siteName
Безусловно можно было сделать просто методы getSiteName и setSiteName (так наверняка бы сделали люди, программировавшие на Яве) и вызвать их в коде, но мне кажется, что семантически обращение к переменной в данном случае выглядит гораздо логичнее (хотя это дело вкуса).

Есть более сложные случаи, когда использование свойств может сильно упростить задачу. Давайте представим, что мы с вами делаем сложное веб-приложение, модель которой, состоит из большого количества разнообразных методов. Пусть это будет билинговая система интернет-провайдера. Нам нужны модули, которые умеют работать с информацией о клиентах, лицевых счетах, тарифах и пр. Причем в веб-интерфейсе приходится получать данные из разных подсистем в разных комбинациях.

Очевидно, что такую систему будет удобно разбить на классы - так мы и поступим. Но остется одна проблема: количество этих классов велико и мы не можем точно сказать какие из них нам понядобятся в веб-интерфейсе. Решение "в лоб": подключить все классы в одном мета-классе и создать объекты.
@CLASS
billing

@USE
clients.p
accounts.p
tariffs.p
...

@create[]
  $clients[^clients::create[]]
  $accounts[^accounts::create[]]
  $tariffs[^tariffs::create[]]
  ...
И использовать методы классов в основной программе:
@USE
billing.p

@main[]
  $billing[^billing::create[]]
  $clients[^billing.clients.all[]]
Вроде все нормально, но мы, в данном случае, используем только модуль clients, но тем не менее компилируем все! Можно, конечно, отказаться от мета-класса, вставить в программу кучу if-ов и подключать только нужные модули. Правда такой подход резко усложнит программу и затруднит отладку. Теперь посмотрим как эту проблему помогут решить свойства.

Перепишем класс billing следующим образом:
@CLASS
billing

@create[]
  $_clients[]
  $_accounts[]
  $_tariffs[]
  ...

@GET_clients[]
  ^if(!def $_clients){
    ^use[clients.p]
    $_clients[^clients::create[]]
  }
  $result[$_clients]

@GET_accounts[]
  ^if(!def $_accounts){
    ^use[accounts.p]
    $_accounts[^accounts::create[]]
  }
  $result[$_accounts]

@GET_tariffs[]
  ^if(!def $_tariffs){
    ^use[tariffs.p]
    $_tariffs[^tariffs::create[]]
  }
  $result[$_tariffs]

...
Теперь наш код будет компилировать модули только, если к нему реально идет обращение из программы. Теперь мы можем не заюотится о своевременной инициализации модулей! Кроме того в такой ситуации мы можем сделать модули вложенными не теряя функциональность и не используя промежуточные переменные. А это в Парсере можно сделать только с помошью свойств, поскольку писать цепочки вызовов методов (в Яве можно так: getBilling().getClients().all()) мы не можем.

Такой подход используется в PFе и решениях на его основе достаточно интенсивно. В заключении хочу напомнить, что свойства еще и хороший способ сделать в классах поля "только для чтения". Настоятельно рекомендую делать свойства для всех переменных класса, к которым могут получать доступ другие классы - помогает при рефакторинге.

Библиотека PF