воскресенье, 17 апреля 2016 г.

Cache': Вызываем метод базового класса

Эта заметка описывает способы вызова метода базового класса на языке Cache ObjectScript.

Положим, что существует два класса, один из которых наследован от другого и наследник переопределяет одну из функций базового класса. В переопределенной функции требуется вызвать такую же функцию базового класса. Положим, имя базового класса BaseClass, имя наследника DerivedClass. Положим, что переопределяемый метод называется Func.
Cache предлагает два способа вызова базового класса, причем эти способы являются зависимыми от версии сервера. Первый способ состоит в смене контекста трансляции, второй в приведении типа объекта. Смена контекста трансляции работает на младших версиях Cache, версии 3 и ее модификации. Приведение типа объекта работает начиная с версии 3.2.2 (видимо, в билд попадают работы по четверке) и старше. В версиях 4 и старше рекомендуется использовать приведение типа.

Смена контекста трансляции выполняется директивами #classcontext / #endclasscontext. В переопределяемом методе следует сменить контекст, в измененном контексте вызвать переопределяемую функцию, вернуть контекст. После чего трансляция продолжается в контексте наследника. Директивы #classcontext / #endclasscontext работают в стековом режиме, #classcontext сохраняет текущий контекст и переключается в указанный, #endclasscontext восстанавливает сохраненный контекст.

Пример вызова метода базового класса в переопределяемом методе с помощью смены контекста трансляции:
method Func()
{
  classmethod = 0;
  not final;
  code =
  {
    w 123,! ; некий код переопределенного метода DerivedClass.Func()
    #classcontext BaseClass
    d ..Func() ; вызов BaseClass.Func() для этого же объекта
    #endclasscontext BaseClass
    w 456,! ; продолжение кода переопределенного метода DerivedClass.Func()
    q
  }
}


Для вызова метода базового класса путем приведения типа используется конструкция вида ##class(=ClassName)##this.MethodName(Arguments). Здесь участвует даже две директивы препроцессора. Способ рекомендуется начиная с версии Cache 4 и старше, но работает также и в версии 3.2.2. Приведение типа есть практически то же самое по отношению к компилятору классов, что и смена контекста трансляции, то есть есть способ указать, что переменную %this (макродиректива ##this) следует рассматривать как относящуюся к заданному классу. Этот способ технически выполнен более грамотно. Действительно, для целей приведения типа (кастинг) он подходит наилучшим образом.

Пример вызова метода базового класса путем приведения типа:
method Func()
{
  classmethod = 0;
  not final;
  code =
  {
    w 123,! ; некий код переопределенного метода DerivedClass.Func()
    d ##class(=BaseClass)##this.Func() ; вызов BaseClass.Func() для этого же объекта
    w 456,! ; продолжение кода переопределенного метода DerivedClass.Func()
    q
  }
}


Различие способов вызова метода базового класса в разных версиях Cache ставит проблему написания кода, переносимого от версии к версии. В этих целях можно использовать либо административный подход, либо программный. В административном подходе просто запрещается использование какой-либо версии, и явно указывается, что писать следует под вполне определенную, например под четвертую. В программном подходе следует использовать конструкции, которые автоматически должны определить версию сервера и использовать соответствующий метод.

Автоматически определить версию сервера можно, используя либо директивы препроцессора, либо выполнив переопределяемые методы в виде генераторов.

Использование директив препроцессора. Используем директивы исполнения при трансляции, например директиву #if:
method Func()
{
  classmethod = 0;
  not final;
  code =
  {
    w 123,! ; некий код переопределенного метода DerivedClass.Func()
    #if $p($$GetVersion^%apiOBJ()," ",4)<4
    #classcontext BaseClass
    d ..Func() ; вызов BaseClass.Func() для этого же объекта
    #endclasscontext BaseClass
    #else
    d ##class(=BaseClass)##this.Func() ; вызов BaseClass.Func() для этого же объекта
    #endif
    w 456,! ; продолжение кода переопределенного метода DerivedClass.Func()
    q
  }
}


Этот способ я не проверял, и привожу его только теоретически. Второй способ является более надежным, поскольку использует технику, являющуюся общей для всех версий Cache.

Использование методов - генераторов. В генераторе просто вставляем определение версии Cache и в зависимости от нее генерируем различный код:
generator =
{
  s %code=0
  $$$GENERATE(" w 123,!") ; некий код переопределенного метода DerivedClass.Func()
  if $p($$GetVersion^%apiOBJ()," ",4)<4 d ; определяем версию
  . $$$GENERATE(" #classcontext BaseClass")
  . $$$GENERATE(" d ..Func()") ; первый способ
  . $$$GENERATE(" #endclasscontext BaseClass")
  e $$$GENERATE(" d ##class(=BaseClass)##this.Func()") ; второй способ
  $$GENERATE(" w 456,!") ; продолжение кода переопределенного метода DerivedClass.Func()
  $$$GENERATE(" q")
  q $$$OK
}

Комментариев нет:

Отправить комментарий