апреля 02, 2011

Контекстные параметры

Часто в программе существуют объекты-менеджеры. Например, гуи-менеджер — объект,
при помощи которого осуществляется управление кнопками, окошками, полями ввода
и т.д. Чтобы создать кнопку, нужно указать в каком именно гуи-менеджере создаём.
Чтобы установить кнопке стиль, нужно этот стиль получить из гуи-менеджера.
  Button := GuiManager.CreateButton;
  Button.SetStyle(GuiManager.GetStyle('glamur'));

При этом в 99% программ название переменной (или свойства), содержащей ссылку на
менеджера, будет называться именно GuiManager.

Поэтому возникает естественное желание организовать всё так, чтобы GuiManager
не прописывать в вызовах вручную, а как-то автоматически подставлять.

Самое тупое решение — создать глобальную переменную GuiManager и пусть к ней
обращаются функции CreateButton, GetStyle и прочие. А что если в программе
нужно работать с несколькими менеджерами? Нужно просто в нужные моменты
менять значение глобальной переменной. Что делать, если нужно работать с
разными менеджерами из разных потоков? Переписать GuiManager с глобальной
переменной под зависящую от потока.

Хоть этот метод и решает поставленную задачу, он мне не нравится. Вместо него
напрашивается концепция «контекстных параметров». Её мне не удалось реализовать
на паскале, но я надеюсь, что получится реализовать в Lisp'е и DEmbro.

Идея вот в чём: у функции мы выделяем некоторый набор параметров и объявляем их
контекстными. Например:
  function CreateButton(context GuiManager: TGuiManager): TGuiButton;
Здесь мы указали, что параметр GuiManager контекстный. От этого мы не теряем
возможностей, функцию можно вызвать обычным образом:
  Button := CreateButton(GuiManager);
при этом выполнится она так же, как и обычная функция.

Отличие заключается в возможности опустить передачу параметра:
  Button := CreateButton();
При этом компилятор автоматически подставит GuiManager внутрь, обратившись
к идентификатору в текущей области видимости. Т.е.

  procedure DoSomething(GuiManager: TGuiManager);
    procedure DoSomethingDeep;
    var
      GuiManager: TGuiManager;
    begin
      Button := CreateButton(); // в CreateButton передастся локальная переменная
                                // функции DoSomethingDeep
    end;
  begin
    Button := CreateButton(); // в CreateButton передастся параметр
                              // функции DoSomething
  end;


Может даже так получится, что GuiManager — это функция, сама содержащая
контекстный параметр:
  var
    App: TApp;

  function GuiManager(context App: TApp): TGuiManager;
  begin
    Result := App.GuiManager;
  end;

  ....

  Button := CreateButton(); // эквивалентно CreateButton(GuiManager(App))



Теперь несколько слов о возможном синтаксисе в паскаль-семействе. Контекстные
параметры поведением очень похожи на параметры со значением по умолчанию. Чтобы
подчеркнуть это, возможно, что имеет смысл заменить синтаксис, использованный выше, на
такой:
  function CreateButton(GuiManager: TGuiManager = context): TGuiButton;


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

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

Постоянные читатели

Обо мне

Моя фотография
Мой e-mail: vitek_03(at)mail(dot)ru