Применение Windows API - страница 15

Шрифт
Интервал

стр.

>template inline T

>GetWinLong(HWND hwnd, int which = GWL_USERDATA) {

> return reinterpret_cast(::GetWindowLong(hwnd, which));

>}


>template inline void

>SetWinLong(HWND hwnd, T value, int which = GWL_USERDATA) {

> ::SetWindowLong(hwnd, which, reinterpret_cast(value));

>}

Мы должны быть, хотя бы внимательными. Прежде всего мы должны освободить контроллер после того, как использовали его. Мы делаем это при обработке WM_DESTROY.

Во-вторых, Windows имеет неудачную идею (привычку) посылать сообщения WM_COMMAND и WM_NOTIFY перед WM_INITDIALOG и после WM_DESTROY. Что можно здесь сказать? Я бы побил менеджера, который ответствен за эти дела. Но раз это есть, мы должны защитить себя, проверяя, является ли ctrl ненулевым перед вызовом OnCommand и OnNotify.

>BOOL CALLBACK ModalDialog::ModalDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

> DlgController* ctrl = GetWinLong(hwnd);

> switch (message) {

> case WM_INITDIALOG:

>  {

>   CtrlFactory *ctrlFactory = reinterpret_cast(lParam);

>   ctrl = ctrlFactory->MakeController(hwnd);

>   SetWinLong(hwnd, ctrl);

>   ctrl->OnInitDialog (hwnd);

>  }

>  return TRUE;

> case WM_COMMAND:

>  if (ctrl && ctrl->OnCommand(hwnd, LOWORD(wParam), HIWORD (wParam))) return TRUE;

>  break;

> case WM_NOTIFY:

>  if (ctrl && ctrl->OnNotify(hwnd, wParam, (NMHDR *)lParam))

>  return TRUE;

>  break;

> case WM_DESTROY:

>  delete ctrl;

>  SetWinLong(hwnd, 0);

>  break;

> }

> return FALSE;

>}

Здесь представлена красота полиморфизма в действии. Объект фабрики создан клиентом, использующим шаблонный класс. Этот объект передается конструктору ModalDialog. ModalDialog передает его процедуре диалога как пустой указатель (дело в том, что он должен пройти через Windows). Процедура Диалога получает его внутри сообщения WM_INITDIALOG как LPARAM. После прохождения пищеварительного тракта Windows он должен быть восстановлен к своей первоначальной форме, переводом его обратно к указателю на CtrlFactory — в базовый класс всех фабрик контроллера.

Когда мы вызываем его виртуальный метод MakeController, мы вызываем метод, переопределенный в шаблонном классе ControllerFactory. Он создает новый объект для класса ActualCtrl, определенного клиентом. Но снова, он возвращает этот объект к нам замаскированный как обобщенный указатель на DlgController. Так всякий раз, когда мы вызываем любой из виртуальных методов ctrl, мы выполняем клиентские переопределения, определенные в классе ActualCtrl. Это лучшее проявление полиморфизма: Вы записываете код, используя обобщенные указатели, но когда код выполнен, он вызывается с очень специфическими указателями. Когда Вы вызываете методы через эти указатели, Вы выполняете специфические методы, обеспеченные клиентом вашего кода.

Вот, что случается с фабрикой объектов, чей фактический класс >ControllerFactory

Передается конструктору ModalDialog как void*
Передаётся от Windows к ModalDialogProcedure как LPARAM
Приведение в ModalDialogProcedure кCtrlFactory*

А вот, что случается с объектными данными, чьим фактическим классом является EditorData.

Передается конструктору фабрики как void*
Приведение в методе AcquireController класса ControllerFactory к EditorData*
Переданный конструктору EditCtrl какEditotData*

Объект класса EditCtrl, созданный в методе MakeController класса ControllerFactory возвращается из него как DlgController* и сохраняется в этой форме как статический член данных ModalDialog.

Если Вы имеете проблемы после моего объяснения, не отчаивайтесь. Объектно ориентированные методы, которые я только описал, трудны, но необходимы. Они названы образцами проектирования. Я настоятельно рекомендую читать книгу: Gamma, Helm, Johnson and Vlissides — Design Patterns, Elements of Reusable Object-Oriented Software или посмотреть Patterns Home Page (домашнюю страницу образцов). Там описано много творческих способов использования полиморфизма, наследования и шаблонизации, чтобы делать программное обеспечение более пригодным для многократного использования.


стр.

Похожие книги