вторник, 28 июля 2015 г.

Коротко. Сделал чудную штуку - переопределение слов

Коротко. Сделал чудную штуку - переопределение слов.

Позволяет делать нечто вроде Duck-Typing, только со статическим контролем типов, где это возможно.

Пусть есть:

UNIT TObjectA // - определяем модуль для работы с TObjectA

USES
 axiom:TObjectA // - используем аксиоматику TObjectA
;

EXPORTS
 axiom:TObjectA // - экспортируем аксиоматику TObjectA наружу в использующие модули

INTEGER FUNCTION A
 TObjectA IN anObj // - объект типа TObjectA
 ...
 Result := anObj SomeCodeA // - вызываем метод SomeCodeA на экземпляре anObj
; // A

...

UNIT TObjectB // - определяем модуль для работы с TObjectB

USES
 axiom:TObjectB // - используем аксиоматику TObjectB
;

EXPORTS
 axiom:TObjectB // - экспортируем аксиоматику TObjectB наружу в использующие модули

INTEGER FUNCTION B
 TObjectB IN anObj // - объект типа TObjectB
 ...
 Result := anObj SomeCodeB // - вызываем метод SomeCodeB на экземпляре anObj
; // A

Функция A работает с объектом типа TObjectA.
Функция B работает с объектом типа TObjectB.

Тогда можно написать:

USES
 TObjectA // - используем аксиоматику TObjectA и axiom:TObjectA
 TObjectB // - используем аксиоматику TObjectB и axiom:TObjectB
;

REDEFINE
 : A
   OBJECT IN anObj // - абстрактный объект
   if ( anObj Is TObjectB ) then
   // - объект типа TObjectB
    ( anObj B ) // - вызываем метод B
   else
    ( anObj inherited ) // - вызываем "ОСНОВНОЙ" метод TObjectA::A
 ; // A

Теперь можно звать так:

TObjectA VAR x1
TObjectB VAR x2
...
x1 A // - тут вызовется метод TObjectA::A
x2 A // - тут вызовется метод TObjectB::B

Можно написать и симметрично:

USES
 TObjectA // - используем аксиоматику TObjectA и axiom:TObjectA
 TObjectB // - используем аксиоматику TObjectB и axiom:TObjectB
;

REDEFINE
 : B
   OBJECT IN anObj // - абстрактный объект
   if ( anObj Is TObjectA ) then
   // - объект типа TObjectA
    ( anObj A ) // - вызываем метод B
   else
    ( anObj inherited ) // - вызываем "ОСНОВНОЙ" метод TObjectB::B
 ; // B

И тогда можно будет написать:

TObjectA VAR x1
TObjectB VAR x2
TObjectA VAR x3
TObjectB VAR x4
...
x1 B // - тут вызовется метод TObjectA::A
x2 B // - тут вызовется метод TObjectB::B
x3 B // - тут вызовется метод TObjectA::A
x4 B // - тут вызовется метод TObjectB::B

Приведённые примеры это всё в Run-Time.

Но если переопределяемым словам поставить атрибут IMMEDIATE и сделать CompileValue вместо прямых вызовов, то получаем статическое переопределение.

Зависимое от типов параметров и компилируемое.

Зачем это всё нужно?

Для адаптации объектной модели для тех людей, которым не особенно важно знать о "тонкостях конкретной объектной модели".

Например для тестировщиков.

Для них, что TEdit, что TvgEdit, что TsomeOtherEdit - ЕДИНЫ, если они "крякают как утка и машут крыльями как утка".

А также для других людей, которые видят систему, через "призму GUI", ну или другие "обобщённые объекты", классификация которых не совпадает с проектной классификацией.

Ну и всё это дело распространяется не только на объекты, но и на "примитивные" типы, такие как INTEGER и STRING.

А также на интерфейсы.

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

Всё зависит от набора включаемых словарей.

Более того - в разных слоях системы можно иметь разные мапинги.

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

Ну и понятно, что REDEFINE - проверяет "обобщённые сигнатуры". На их ковариантность.

И REDEFINE это не OVERRIDE.

REDEFINE не "встраивается" внутрь классов. Он - "сбоку". Для "внешних пользователей".

Он что-то типа helper'а или категории в Objective-C.

При этом - повторю - для каждого конкретного уровня абстракции могут быть СВОИ REDEFINE.

Вот люди там пытаются "писать препроцессоры" - Ссылка. Why can't an interface have class methods?

Цитата:

"Yes, I get that, I guess I was stuck in the mindset of the Java default method and the static method. This is sort of a mix between the two.
The default methods are way cool.
Interface helpers and interface operator overloading would go a long way towards achieving these goals.
(Then there's virtual methods for records, method inheritance for records, allowing multiple class helpers in scope, allowing
inheritance for record helpers).

I'm temped to write a pre-processor to add these things myself (borrow some syntax from oxygene or SMS and transparantly alter the sourcecode before compilation)."

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

Которые ещё и компилируются. Хотя бы в "шитый-код".

И встраивать "различные трансформации аксиоматики" выше, чем код на "целевом языке".

+Виктор Морозов
+Михаил Костицын
+Дмитрий Инишев
+Михаил Морозов
+Андрей Трофимов

P.S. Понятное дело, что с помощью подобных "штучек" можно учинить "чудный бардак" в коде, но на любителей "стрелять себе в ногу" - я всё же как-то не ориентируюсь.

Выстрелить себе в ногу можно и в Delphi и в C++ и в Haskel.

А уж в Python... С его слотами.

Тут главное понимать что делаешь и наличие желания "не стрелять".

Ну и понятное дело, что все REDEFINES вычисляются с учётом всех USES и EXPORTS - Коротко. Сделал экспорт словарей.

Ну и если слово не может быть ТОЧНО определено (т.е. существуют два и более кандидатов), то на этапе компиляции выводится сообщение об ошибке.

Тогда слово надо квалифицировать как - UNIT :: WORD.

Ну в полной аналогии с namespace из C++.

Ну и есть ещё один "побочный профит" от всей этой истории - у нас в мета-модели бывают переопределения стереотипов - один уровень мета-модели может переопределять стереотипы другого (более низкого) уровня.

Да.

Вот кстати "пример из жизни":
Коротко. Сделал описание части аксиоматики на Dephi, а части - на скриптах

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Библиотека "ScriptEngine$VT"
// Модуль: "vtComboBoxWordsPack.rc.script"
// Скрипты в ресурсах (.rc.script)
// Generated from UML model, root element: ScriptKeywordsPack::Class Shared Delphi::ScriptEngine$VT::vtComboBoxWords::vtComboBoxWordsPack
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//#UC START# *54EC8C7C011Eimpl*

// Декораторы для слов работающих с комбо

USES
 axiom:ComboBox
 axiom:ComboTree
;

REDEFINITION
 : pop:ComboBox:DropDown
  OBJECT IN aCombo
  if ( aCombo Is class::TvtComboTree ) then
   ( aCombo pop:ComboTree:DropDown )
  else 
   ( aCombo inherited )
 ; // pop:ComboBox:DropDown
 
REDEFINITION
 : pop:ComboBox:GetItemIndex
  OBJECT IN aCombo
  if ( aCombo Is class::TvtComboTree ) then
   ( aCombo pop:ComboTree:GetItemIndex )
  else 
   ( aCombo inherited )
 ; // pop:ComboBox:DropDown
 
REDEFINITION
 : pop:ComboBox:IndexOf
  OBJECT IN aCombo
  if ( aCombo Is class::TvtComboTree ) then
   ( aCombo pop:ComboTree:IndexOf )
  else 
   ( aCombo inherited )
 ; // pop:ComboBox:DropDown
 
REDEFINITION
 : pop:ComboBox:SaveItems
  OBJECT IN aCombo
  if ( aCombo Is class::TvtComboTree ) then
   ( aCombo pop:ComboTree:SaveItems )
  else 
   ( aCombo inherited )
 ; // pop:ComboBox:SelectItem
 
REDEFINITION
 : pop:ComboBox:SelectItem
  OBJECT IN aCombo
  if ( aCombo Is class::TvtComboTree ) then
   ( aCombo pop:ComboTree:SelectItem )
  else 
   ( aCombo inherited )
 ; // pop:ComboBox:SelectItem
 
REDEFINITION
 : pop:ComboBox:SetItemIndex
  OBJECT IN aCombo
  if ( aCombo Is class::TvtComboTree ) then
   ( aCombo pop:ComboTree:SetItemIndex )
  else 
   ( aCombo inherited )
 ; // pop:ComboBox:SetItemIndex
 
//#UC END# *54EC8C7C011Eimpl*

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

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