Modularization was an important component in large project management before object-oriented design. Abstract factories can be used to help modularize the creation of a large project, by allowing the actual operations on objects to be completely removed from the main program. The largest problem with this is that the menus for the main program are usually set using some type of resource editor. Once menus are hardwired by this process it is more difficult to change the choices. In projects where the choices are fluid, this can require frequent recompiles of the entire program. You will see how to dynamically create menus and associate menu items with handlers using abstract factories.
Much of this problem is similar to the one you faced with the bank problem . You must provide the choices as strings much the way you did when you created new bank accounts . This can be accomplished by providing a function that returns strings describing the menu items. The actual operations will be encapsulated as classes descendent for an abstract base class. Each class will have a virtual function to carry out the actual operation. The new problem here is that you have to asssociate these operations with the menu items.
Each window system is slightly different, but all rely on menu items listed in some order in a menu. You will use this to implement your dynamic menu. Each menu item must be initialized with its own string, so the menu choice function that returns the menu choices as a TStringArray. This places the proper strings in the menu, but does nothing to associate the menu item with a handler. The trick here is to associate the same handler with each choice, and in that handler use the number of the menu item selected as the parameter in the abstract factory.
__fastcall TSDIAppForm::TSDIAppForm(TComponent *AOwner)
: TForm(AOwner)
{
TStringList *menu = Transaction::menuList(); //retrieve menu strings
for (int i = 0;i < menu->Count;i++)
{
TMenuItem *NewItem = new TMenuItem(TransactionMenu); // create the menu item
NewItem->Caption = menu->Strings[i]; //Associate String with item
NewItem->OnClick = transactionClick; //associate the generic handler
TransactionMenu->Add(NewItem); //add item to menu
}
_accounts.read();
Invalidate();
}
//---------------------------------------------------------------------
// The generic handler
void __fastcall TSDIAppForm::transactionClick(TObject *Sender)
{
int which = AcctListBox->ItemIndex; // which item does the operation apply to
if (which >=0)
{
Transaction* trans = Transaction::newTransaction(((TMenuItem*)Sender)->MenuIndex); //which menu item was selected used to select operation
AcctPtr temp = _accounts[which]; //make a copy of the item to update
trans->upDate(temp); // do the actual operation
_accounts.deleteItemAt(which); // delete original value
_accounts.insertItemAt(temp,which); // insert new value
Invalidate(); // repaint main window
delete trans; // delete the transaction
}
}
//---------------------------------------------------------------------
Construction of this factory is very similar to that of the Account hierarchy . The code is found in the bank project . One difference is the function that returns the interface strings. Your menu should look like this.
To place these choices in the menu create a function which adds each choice to a TStringList.
TStringList* Transaction::menuList()
{
TStringList *menu = new TStringList;
menu->Sorted = false;
menu->Add("&Deposit");
menu->Add("&Withdrawal");
menu->Add("Get &Balance");
return menu;
}
The factory itself is boiler plate.
Transaction* Transaction::newTransaction(int i)
{
Transaction* type[]={&Deposit(),&Withdrawal(),&CheckBalance()};
return type[i]->clone();
}