Delphi 3. Библиотека программиста

       

Абстрактные, контролируемые инеконтролируемые виды


Как видно из дерева наследования на рис. 10.5, интерфейсные формы используются в проекте EmbeddedForms для создания двух категорий видов: неконтролируемы х (TValidView), для которых свойство Valid всегда равно True, и контролируемы х (TFickleView), для которых свойство Valid может изменять ся. Неконтролируемые виды можно использовать для Memo-полей с произвольным содержимым или, например, начальной или завершающей панелей мастера. Контролируемые виды должны применяться каждый раз, когда пользователь может ввести неверные данные, не подлежащие сохранению, — например, дату 31 февраля, расходы, превышающие общую сумму бюджета, и т. д. Поскольку в обоих случаях реализуется интерфейс IView, можно воспользоваться универсальным кодом для работы с обобщенным набором видов.

Рис. 10.5. Интерфейсные формы в проекте

Обе категории видов происходят от класса TAbstractView (листинг 10.4).

Листинг 10.4. Модуль VIEWS.PAS

unit Views; // Copyright © 1997 by Jon Shemitz, //all rights reserved. // Permission is hereby granted to //freely use, modify, and // distribute this source code PROVIDED //that all six lines of // this copyright and contact notice are //included without any // changes. Questions? Comments? //Offers of work? //mailto:jon@midnightbeach.com // ---------------------------------------------- // Отображает соглашение IView на внедренную //форму. Виды обычно // порождаются от TValidView или TFickleView. interface uses Models, Embedded; type TAbstractView = class(TEmbeddedForm, IView) procedure FormCreate(Sender: TObject); private fReadOnly: boolean; protected function GetValid: boolean; virtual; abstract; procedure SetValid(Value: boolean); virtual; abstract; function GetReadOnly: boolean; virtual; procedure SetReadOnly(Value: boolean); virtual; public procedure ReadFromModel(Model: TModel); virtual; procedure WriteToModel(Model: TModel); virtual;

procedure AddNotifiee( Notify: IFrame); virtual; abstract; procedure RemoveNotifiee(Notify: IFrame); virtual; abstract; property Valid: boolean read GetValid write SetValid; property ReadOnly: boolean read fReadOnly write SetReadOnly; end; TViewClass = class of TAbstractView; implementation {$R *.DFM} function TAbstractView.GetReadOnly: boolean; begin Result := fReadOnly; end; // TAbstractView.GetReadOnly procedure TAbstractView.SetReadOnly(Value: boolean); begin fReadOnly := Value; Enabled := not Value; // Вид, доступный только для чтения, отображает // информацию, но не позволяет изменять ее; // вы можете переопределить SetReadOnly, // чтобы изменить визуальное представление таких видов. end; // TAbstractView.SetReadOnly procedure TAbstractView.ReadFromModel(Model: TModel); begin end; // TAbstractView.ReadFromModel procedure TAbstractView.WriteToModel(Model: TModel); begin end; // TAbstractView.WriteToModel procedure TAbstractView.FormCreate(Sender: TObject); begin inherited; _AddRef; // Чтобы Self можно было передавать // как интерфейсную ссылку end; end.

TAbstractView разделяет протокол IView на три части — доступность только для чтения, проверка корректности, обмен данными с моделью — и обрабатывает каждую часть отдельно:

реализация базовой функциональности Read-Only — пользователи не могут изменить данные на заблокированной форме, хотя на практике виды обычно переопределяют метод SetReadOnly, чтобы изменить визуальное представление видов, доступных только для чтения;
вся реализация проверки возлагается на потомков TValidView и TFickleView;
для ReadFromModel и WriteFromModel предоставляются фиктивные заглушки. Поскольку эти методы переопределяются в любом реальном объекте вида, желательно, чтобы виды всегда вызывали inherited.

Как нетрудно догадаться по названию, предполагается, что вы не станете непосредственно использовать класс TAbstractView или напрямую наследовать от него. Вместо этого следует пользоваться TValidView и TFickleView.

Разумеется, все «абстрактные», «неконтролируемые» и «контролируемые» виды можно было свалить в единый класс TView. Разделение обладает двумя основными достоинствами: поскольку неконтролируемые виды игнорируют те части протокола IView, которые занимаются проверкой, программа работает немного быстрее и требует меньше памяти. Что еще важнее, при порождении конкретного вида от TValidView вместо TFickleView свойство Valid всегда остается равным True, даже если вы по неосторожности присвоите ему False (сравните листинги 10.5 и 10.6).

Листинг 10.5. Методы проверки корректности из модуля VALIDVIEWS.PAS

function TValidView.GetValid: boolean; begin Result := True; end; // TValidView.GetValid procedure TValidView.SetValid(Value: boolean); begin // TValidView всегда корректен - //игнорируем Value end; // TValidView.SetValid procedure TValidView.AddNotifiee(Notify: IFrame); begin // TValidView всегда корректен - игнорируем запрос на добавление end; // TValidView.AddNotifiee procedure TValidView.RemoveNotifiee(Notify: IFrame); begin // TValidView всегда корректен - игнорируем запрос на удаление end; // TValidView.RemoveNotifiee

Листинг 10.6. Фрагмент модуля FICKLEVIEW.PAS

type TFickleView = class(TAbstractView) private { Private declarations } fValid: boolean; fNotify: IFrame; // В данной реализации проверки //корректности поддерживается // всего один получатель уведомлений public { Public declarations } procedure AddNotifiee( Notify: IFrame); override; procedure RemoveNotifiee(Notify: IFrame); override; function GetValid: boolean; override; procedure SetValid(Value: boolean); override; end; implementation {$R *.DFM} procedure TFickleView.AddNotifiee(Notify: IFrame); begin fNotify := Notify; end; // TFickleView.AddNotifiee procedure TFickleView.RemoveNotifiee(Notify: IFrame); begin fNotify := Nil; end; // TFickleView.RemoveNotifiee function TFickleView.GetValid: boolean; begin Result := fValid; end; // TFickleView.GetValid procedure TFickleView.SetValid(Value: boolean); begin if Value <> fValid then begin fValid := Value; if Assigned(fNotify) then fNotify.OnValidChanged(Self, Self); end; // Value <> fValid end; // TFickleView.SetValid

Содержание раздела