开发者

Globally-accessible data storage in a menu driven program?

I'm new to Stack Overflow but I find myself seeking some of the best programming solutions on this site. So I have a question to ask.

I am writing a program in Delphi that is a TUI menu-driven program for a local business client. They have asked me to keep the user interface the same as in the old program (written in BASIC for MS-DOS, dated in 1982) so it is all menu driven with global data being stored in files and reloaded by the program. Each sub-menu is a program in and of itself run by the active menu (also a program).

I have written my own TUI framework and UI manager for displaying menus and sub-menus. The UI manager contains an overridden method called "Draw" to display the menu and another overridden method called "OnEvent" which handles keyboard events in the UI. My first question is would you consider this to be an appropriate method for making a menu-driven program containing sub-menus? An example of how this works is such:

type
  TMenu1 = class(TExtendedUIManager)
  private
    procedure OnEvent (c: Char); override;
  end;

type
  TSubMenu1 = class(TExtendedUIManager)
  end;

procedure TMenu1.OnEvent (c:开发者_开发百科 Char);
var
  Next: TExtendedUIManager;
begin
  if c = '2' then begin
    Next := TSubMenu1.Create;
    Self.Start(Next);
    Next.Free;
  end;
end;

My other question is what would be an appropriate way of sharing data between menus? For example, if I wanted my TSubMenu1 class to return a string when a method is called, how would I make it accessible to other sub-menus that do not interact with it? (Sorry if the question is vague). I have the Singleton pattern in mind but I've also thought of having the UI manager store a reference to some object for data storage and each time a new sub-menu is run, pass in the reference to the new sub-menu (UI manager). The conundrum is finding out which one works best. Or even if my menu-driven framework is decent.

Opinions are welcomed and any advice is appreciated. Thanks for your time and help!

--Todd


"My other question is what would be an appropriate way of sharing data between menus?"

You can share the data by using class methods and properties. By using these, you can even access them even without create instance of the class. For more info go through this Link.

Below is sample code which will share List.

type
  TForm1 = class(TForm)
    ---
    ---
  private
    { Private declarations }
    class var List: TStringList;
    ---
  end;
var
  Form1, Form2: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.List.Add('4');
  Form2.List.Add('5');

  ShowMessage(TForm1.List.Text);
end;

initialization
  StrList := TStringList.Create;
  TForm1.List := TStringList.Create;
  TForm1.List.Add('1');
  TForm1.List.Add('2');
  TForm1.List.Add('3');
  ShowMessage(TForm1.List.Text);


finalization
  FreeAndNil(TForm1.List);
end.


I'll freely admit this is thinking aloud territory for me, but the first thing that caught my eye was this:

procedure TMenu1.OnEvent (c: Char);
var
  Next: TExtendedUIManager;
begin
  if c = '2' then begin
    Next := TSubMenu1.Create;
    Self.Start(Next);
    Next.Free;
  end;
end;

It feels like the end condition of this sort of programming is going to be huge decision trees of if c = '2' then ... else if c = '3' then ... else and so on. This does get tedious to write and maintain; if feasible, an array mapping input character with a function to execute is often far easier to maintain.

[['2', foo_create],
['3', foo_delete],
['4', foo_ship],
['d', foo_destroy_all],
['`', foo_return_to_previous_menu]]

When a new character comes in, you would look up the corresponding function in the table and execute it. Once you're done, return to waiting.

You could further extend this array to keep track of arguments that are required and values returned from each function. When you execute a function, store its return value in a global variable somewhere, and keep track of what types were returned. (Perhaps Delphi's type system is sophisticated enough that it is trivial, and not even worth mentioning.) When you execute a new function, check to see if the type of the 'current' returned result is suitable to pass to the desired function. (You could even grey-out the menu entries if the types are wrong, to indicate to the user that this combination won't work.)

/* key function    arg   ret types */
[['2', foo_create, NULL, FOO],
['3', foo_delete, FOO, NULL],
['4', foo_ship, FOO, NULL],
['d', foo_destroy_all, NULL, NULL],
['`', foo_return_to_previous_menu, NULL, NULL]]

I hope this is useful in some fashion. :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜