unit CommandFacade; interface uses Contnrs, StdCtrls, Document, DocumentCommands; type TCommandFacade = class(TObject) private FCommandList : TObjectList; FCurrCommand : Integer; // Possibly this should be registered, rather than created here FDocument : TDocument; FMemo : TMemo; procedure ClearOldCommands; procedure PerformCommand(CommandClass : TDocumentCommandClass; ClearCommandList : Boolean); procedure UpdateMemo; protected public constructor Create; destructor Destroy; override; procedure OpenDocument; procedure CloseDocument; procedure Undo; procedure Redo; procedure SearchAndReplace; procedure PrettyPrint; // This allows a memo control to be updated when the document content // changes. This should really be an Observer etc, but the session is // only so long! procedure RegisterMemo(AMemo : TMemo); end; var Commands : TCommandFacade; implementation uses SysUtils, Math; var InstanceCount : Integer = 0; { TCommandFacade } constructor TCommandFacade.Create; begin if InstanceCount >= 1 then begin raise Exception.Create('Only one instance of TCommandFacade allowed'); end; inherited; FCommandList := TObjectList.Create(True); // Frees the list objects itself FCurrCommand := -1; FDocument := TDocument.Create; Inc(InstanceCount); end; destructor TCommandFacade.Destroy; begin FDocument.Free; FCommandList.Free; Dec(InstanceCount); inherited; end; procedure TCommandFacade.ClearOldCommands; var i : Integer; begin for i := FCommandList.Count - 1 downto FCurrCommand + 1 do begin FCommandList.Delete(i); end; end; procedure TCommandFacade.PerformCommand(CommandClass : TDocumentCommandClass; ClearCommandList : Boolean); var NewCommand : TDocumentCommand; begin NewCommand := CommandClass.Create(FDocument); try NewCommand.Execute; if ClearCommandList then begin FCommandList.Clear; end else begin // If have done an undo and then choose a new command, clear the // old following commands ClearOldCommands; FCurrCommand := FCommandList.Add(NewCommand); end; except // Only add command to the command list if doesn't raise an exception NewCommand.Free; end; end; procedure TCommandFacade.UpdateMemo; begin if Assigned(FMemo) then begin FMemo.Lines.Text := FDocument.Text; end; end; procedure TCommandFacade.OpenDocument; begin PerformCommand(TOpenCommand,True); UpdateMemo; end; procedure TCommandFacade.CloseDocument; begin PerformCommand(TCloseCommand,True); UpdateMemo; end; procedure TCommandFacade.Redo; begin if FCurrCommand < FCommandList.Count - 1 then begin Inc(FCurrCommand); if InRange(FCurrCommand,0,FCommandList.Count - 1) then begin TDocumentCommand(FCommandList[FCurrCommand]).Execute; UpdateMemo; end; end; end; procedure TCommandFacade.Undo; begin if InRange(FCurrCommand,0,FCommandList.Count - 1) then begin TDocumentCommand(FCommandList[FCurrCommand]).Rollback; UpdateMemo; if FCurrCommand > -1 then begin Dec(FCurrCommand); end; end; end; procedure TCommandFacade.SearchAndReplace; begin PerformCommand(TSearchAndReplaceCommand,False); UpdateMemo; end; procedure TCommandFacade.PrettyPrint; begin PerformCommand(TPrettyPrintCommand,False); UpdateMemo; end; procedure TCommandFacade.RegisterMemo(AMemo : TMemo); begin FMemo := AMemo; end; initialization Commands := TCommandFacade.Create; finalization FreeAndNil(Commands); end.