Часто в программе существуют объекты-менеджеры. Например, гуи-менеджер — объект,
при помощи которого осуществляется управление кнопками, окошками, полями ввода
и т.д. Чтобы создать кнопку, нужно указать в каком именно гуи-менеджере создаём.
Чтобы установить кнопке стиль, нужно этот стиль получить из гуи-менеджера.
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;
Комментариев нет:
Отправить комментарий