Functors

Introduction

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.

Basic Idea

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.

Menus and 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.

Menu Initialization and Handler

__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 
    }
}
//---------------------------------------------------------------------

The Abstract Factory

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();
}