2014-02-16 37 views
2

我正在爲我的所有應用程序模塊使用某種(類似於MVA的)模式。如何在視圖非模態時在視圖之前銷燬適配器?

視圖是TForm後代:

TSomeView = class(TForm) 
    ... 
end; 

數據在模型管理:

TSomeModel = class 
public 
    property DataSet: TDataSet read ...; 
end; 

視圖和模型是通過一個適配器粘在一起。

uses 
    Some.Model, Some.View; 

type 
TSomeAdapter = class 
private 
    FView : TSomeView; 
    FModel : TSomeModel; 
    procedure ClickHandler(Sender: TObject); 
public 
    constructor Create(AOwner: TComponent); 
    destructor Destroy; override; 
    procedure Run; 
end; 

雖然這看起來有點乏味,但它可以很好地分離事物。

到現在爲止,我一直使用模式表單,從而使適配器實現看起來是這樣的:

constructor TSomeAdapter.Create(AOwner: TComponent); 
begin 
    inherited Create; 
    FModel := TSomeModel.Create, 
    FView := TSomeView.Create(AOwner); 
    FView.DataSource.DataSet := FModel.DataSet; 
    FView.SomeButton.OnClick := ClickHandler; 
end; 

procedure TSomeAdapter.Run; 
begin 
    FView.ShowModal; 
end; 

destructor TSomeAdapter.Destroy; 
begin 
    FView.Free; 
    FModel.Free; 
    inherited; 
end; 

這裏最重要的是,我不要斷開事件處理程序和數據集在析構函數,因爲視圖總是首先被銷燬。

呼叫者創建使用這種模式的應用模塊:

procedure CallSome; 
var 
    Adapter: TSomeAdapter; 
begin 
    Adapter := TSomeAdapter.Create(...); 
    try 
    Adapter.Run; 
    finally 
    Adapter.Free; 
    end; 
end; 

我想這個適應非模態形式。

調用者不能釋放適配器,因爲它不知道何時。所以調用者的代碼現在看起來是這樣:

procedure CallSome; 
var 
    Adapter: TSomeAdapter; 
begin 
    Adapter := TSomeAdapter.Create(...); 
    Adapter.Run; 
    // Adapter is now a memory leak 
end; 

我不想改變破壞秩序,因爲我依靠的事實,我不需要斷開處理程序和數據集。

如何在使用非模態窗體時保留銷燬順序(查看<模型<適配器)?

+1

在你的模式方法,你只能有一個觀點,你將會收到通知 - >一個適配器。現在你有多個適配器。組織列表中的多個元素 –

+0

不僅如此,您可能無法再直接在調用程序中創建適配器,可能已經有一個適配器正在運行。看起來它會變得更加單調乏味...... –

+1

你可能想要考慮以下幾點:a)把'TSomeAdapter'改成一個更通用的類,它會注入'TSomeView'和'TSomeModel',所以它不需要知道它們。這意味着兩者都需要從一個抽象類派生或實現一個接口。 b)考慮將它們注入已經創建的,但如果構造函數不總是相同的話,那可能會造成問題。 c)創建一個保存對所有創建的「TSomeAdapter」的引用的類。 d)使'TSomeAdapter'完成時通知持有者類,以便它可以被釋放。 –

回答

1

傳遞一個主窗口句柄所有適配器:

procedure CallSome; 
var 
    Adapter: TSomeAdapter; 
begin 
    Adapter := TSomeAdapter.Create(..., FMainView.Handle); 
    FMainAdapter.Add(Adapter); 
    Adapter.Run; 
end; 

連接OnClose並設置CloseActioncaNone

procedure TSomeAdapter.ViewClose(Sender: TObject; var CloseAction: TCloseAction); 
begin 
    CloseAction := TCloseAction.caNone; 
    PostMessage(FMainViewHandle, WM_FREE_ADATER, NativeUInt(@Self), 0); 
end; 

使用TObjectList主適配器和處理WM_FREE_ADAPTER:

constructor TMainAdapter.Create(...); 
begin 
    inherited; 
    FAdapters := TObjectList.Create; 
end; 

procedure TMainAdapter.WMFreeAdapter(var Msg: TMessage); 
begin 
    FAdapters.Remove(PAdapter(Msg.WParam)^); 
end; 

destructor TMainAdapter.Destroy; 
begin 
    FAdapters.Free; 
    inherited; 
end; 
3

你哈已經改變你的適配器,以便適​​配器是視圖的所有者,並使視圖自毀。適配器會在視圖被銷燬時得到通知。

type 
    TSomeAdapter = class(TComponent) 
    private 
    FView : TSomeView; 
    FModel : TSomeModel; 
    FOnEndsRunning : TNotifyEvent; 
    function GetModel : TSomeModel; 
    function GetView : TSomeView; 
    function GetIsRunning : Boolean; 
    protected 
    procedure Notification(AComponent : TComponent; Operation : TOperation); override; 
    property View : TSomeView read GetView; 
    property Model : TSomeModel read GetModel; 
    procedure ClickHandler(Sender : TObject); 
    public 
    destructor Destroy; override; 
    procedure Run; 
    property IsRunning : Boolean read GetIsRunning; 
    property OnEndsRunning : TNotifyEvent read FOnEndsRunning write FOnEndsRunning; 
    end; 

{ TSomeAdapter } 

destructor TSomeAdapter.Destroy; 
begin 
    FModel.Free; 
    inherited; 
end; 

procedure TSomeAdapter.ClickHandler(Sender : TObject); 
begin 
    // DoSomething 
end; 

function TSomeAdapter.GetIsRunning : Boolean; 
begin 
    Result := Assigned(FView); 
end; 

function TSomeAdapter.GetModel : TSomeModel; 
begin 
    // lazy initialization of model 
    if not Assigned(FModel) then 
    FModel := TSomeModel.Create; 
    Result := FModel; 
end; 

function TSomeAdapter.GetView : TSomeView; 
begin 
    // lazy initialization of view 
    if not Assigned(FView) then 
    begin 
    FView := TSomeView.Create(Self); // Owner is this Adapter 
    FView.DataSource.DataSet := Model.DataSet; 
    FView.SomeButton.OnClick := ClickHandler; 
    end; 
    Result := FView; 
end; 

procedure TSomeAdapter.Notification(AComponent : TComponent; Operation : TOperation); 
begin 
    inherited; 
    case Operation of 
    opInsert : 
     ; 
    opRemove : 
     if AComponent = FView then 
     begin 
     // forget the view reference 
     FView := nil; 
     // destroy the model (optional) 
     FreeAndNil(FModel); 
     // notify 
     if Assigned(OnEndsRunning) then 
      OnEndsRunning(Self); 
     end; 
    end; 

end; 

procedure TSomeAdapter.Run; 
begin 
    View.Show; 
end; 

的觀點應該釋放本身關閉

TSomeView = class(TForm) 
    procedure FormClose(Sender: TObject; var Action: TCloseAction); 
end; 

procedure TSomeView.FormClose(Sender: TObject; var Action: TCloseAction); 
begin 
    Action := caFree; 
end; 

現在,如果適配器結束運行