Notes for week 10

Abstract Factories and DLLs

Introduction

A bank wants to implement a program that will create new accounts, edit account information, service deposites and withdrawals and retrieve the current balance. When creating the new account the bank must record the persons social security number and determine the method for distribution of interest. While this is certainly not a complete list of banking functions, there are enough different operations to show how an abstract factory as a dll.

One consideration in this problem is the number of different types of accounts and the frequency at which new account types are created and old types discontinued. You will create a dll which exports exactly one class, Account, while implementing a class for each type of account offered by the bank. you must decide on which types of information your interface will collect in the most general type of account, and make provision for disabling those interface aspects that do not apply to a given type of account. The method of interest dispersal is information that is needed for CDs, so is included in the interface. For accounts that do not give interest or which automatically redeposit interest in the account, the interface item that collects this information is disabled.

You were introduced to abstract factories in week 11 of 266371 . As part of the discussion you not only learned how to create classes that using a "virtual" constructor, you also learned that these classes could represent functors, that is a set of distinct operations. You used these functions to implement abstract menus, but this was a bit of overkill, since you would have to alter the main program every time you changed the set of functions. In this section you will learn to make the main program independent from the classes created by the abstract factory by using dlls .

Some Basic Considerations

In this program you can associate a click on the menu header with an empty handler and when you create the menu interactively associate each item in the menu with one handler that you create.In order to create these menus, you must have a list of the names of the different types of accounts (and for the second menu the types of transactions).These functions must be static functions in the abstract base class Account. You will notice one change from the implementation last semester. The bank classes no longer use the ANSI string class, they use the generic string and char * types. This is so the dll that contains them will travel better (between implementation platforms-read different compilers.)

The Implementation

The Account Class

The account class is now defined as follows.

#include 
#if defined(ACCOUNT_TESTDLL)
#define ACCOUNT_CLASS class
#elif defined (ACCOUNT_BUILDDLL)
#define ACCOUNT_CLASS class _export
#else
#define ACCOUNT_CLASS class _import
#endif
 

ACCOUNT_CLASS Account
{
public: 
	Account();
	Account(const char*  name,const char * ssNumber,const char * acctNumber,int  dispersal,double  balance);
	void  setValues(const char * name,const char * ssNumber,const char * acctNumber,int  dispersal,double  balance);
	virtual void  deposit(double,char* );//return a statement of current balance  
	virtual int withdrawal(double,char* );
	virtual int canWithdraw()=0;
	virtual int canDeposit()=0;
	virtual int interestDispersal()=0;
	//The following are used to initialize dialog with account information  
	void getBalance(char *);// character string with balance  
	void getName(char *);//get name  
	void getAcctNumber(char *);// get account number  
	void getSSNumber(char *);//get social security number  
	void getDispersal(char *);//get method used to distribute interest  
	void setDispersal(int );//set the mode of interest dispersal.  
   	int  getDispersal();//when number is needed  
   	virtual void  printString(char  *);           
	virtual void  getType(char *)=0;
	// When information edited, the whole account record is deleted and
	// replaced with a new record. Account number is fixed and balance no
	// shown. Balance only altered by withdraw and deposit.  
	static  Account* create(int  i); // used to create a counted pointer in main  
	static  void getKinds(char  *);
   	static char* getKindString(int );
	static void getDispersals(char  *);
   	static char*  getDispersalString(int );
private: 
	static char*  _kind[];
	static char*  _dispersals[];
	double  _balance;
	string _acctNumber;
	string _name;
	string _ssNumber;
	int  _dispersal;
	virtual  Account* clone()=0;
};

The static functions are still much as they were last semester except for the fact you are now creating char* results. Here are a sampling of these functions.

char * Account::_kind[]={"Savings" ,"Checking" ,"CD" ,"NULL" };
char * Account::_dispersals[]={"Redeposit" ,"Transfer" ,"Check" ,"NULL" };
Account* Account::create(int i)
{
	Account* type[]={&Savings(),&Checking(),&CD()};
	return  type[i]->clone();
}

void  Account::getKinds(char  *buff)
{
	buff[0]='\0';
	int i=0;
	while  (strcmp(_kind[i],"NULL" ))
	{
		strcat(buff,_kind[i]);
      		strcat(buff,"\n");
		i++;
	}
}

Notice that once you get passed the factory, the string generator will work now matter how many strings you add to the list.

The Interface

When the program is create the menus Bank Account and Transaction are left empty.

The constructor of the main form must initialized the menus using the static functions from Account that list the types of accounts and transactions as strings.

__fastcall  TMainForm::TMainForm(TComponent* Owner)
	: TForm(Owner)
{
   TStringList *menu = new  TStringList;
   menu->Sorted = false ;
   char buff[1000];
   Account::getKinds(buff);
   menu->Text=buff;
   for  (int  i = 0;i< menu->Count;i++)
    {
        TMenuItem *NewItem = new  TMenuItem(BankMenu); // create the separator  
        NewItem->Caption = menu->Strings[i];
        NewItem->OnClick = NewAccountItemClick;
        BankMenu->Add(NewItem);
    }
    menu->Clear(); //clear TStringList 
    buff[0]='\0';//clear Buffer  
    Account::getDispersals(buff);
    menu->Text=buff;
    for  (int i = 0;i< menu->Count;i++)
    {
        TMenuItem *NewItem = new  TMenuItem(TransactionsMenu); // create the separator  
        NewItem->Caption = menu->Strings[i];
        NewItem->OnClick = TransactionsMenuClick;
        TransactionsMenu->Add(NewItem);
    }
}

Once this constructor is called, the menus have choices.The there is no handler for the BankMenu, while the handler for each item (which you have to create since you can't click on items that don't exits) makes sure the correct action takes place.

void __fastcall  TMainForm::NewAccountItemClick(TObject *Sender)
{

      int  itemPicked = ((TMenuItem*)Sender)->MenuIndex;
      AcctDlg->Caption = "New " + String(Account::getKindString(itemPicked))+" Account " ;
      char  buff[1000];
      Account::getKinds(buff);
      AcctDlg->AccType->Items->Text= buff;
      AcctDlg->AccType->ItemIndex=itemPicked;
      AcctDlg->AccType->Enabled=false ;
      if  (AcctDlg->ShowModal()==IDOK)
      {
      //use the account factory to create an account
      //and setValues to enter the correct values.
  
      }
}

Complete Code

To get the complete code click here .

Help Using HTML

Introduction

There are two standard methods used to add help to your projects. You may either use the Windows help engine or HTML. HTML is rapidly becoming the method of choice to distribute help, since it is easy to view this help from outside as well as inside the program. HTML files are also easy to create because of the large number of HTML editors available. The main problem you have is how to view an HTML file in your program.

The Implementation of Help Dialog

First create you help dialog. You can eliminate the cancel button if you wish (I didn't here). Next place you ok button on a panel. (Drop a panel on the dialog, delete the caption, align it to the bottom and send it to the rear. Your ok button should now be in the panel. At this point find the html component in the internet pallet.

This HTML component can access remote sites on the internet, but you will use it only to show your help files (although if you have external links in your help files these will be displayed to the limit of this viewer (and lack of plugins). To display a file you need only execute the following function call

HTML->RequestDoc("file:///"  + file);

where file is a String. The easiest way to do this is to add the following constructor to your help dialog.

__fastcall  THelpDialog::THelpDialog(String file,TComponent* Owner)
    : TForm(Owner),_fileName(file)
{
     if  (file != "" )
        HTML->RequestDoc("file:///"  + file);
}

You help dialog might look like this when it is done.

Using The Help Dialog

Once you have a help dialog, you will call it from various places in your program. You will call it from the help menu item (or speed button) in the main program and you may want to connect it to the f1 button. There are a few worries about this. First the string file you pass into the dialog will only contain a file name. The path will vary from user to user since not everyone will install your program in the same place. This means before you call on help you must adjust the current directory to be the location of the help files. In addition, you must restore the existing path after you have finished with help. This is important because the user is working in a given directory and doesn't want to have to find it after the fact. This code does not restore the path, but you can extend it easily to do this. To make all of this work, you should change the status of this dialog from autocreate to make available in the Forms tab of the Projects/Options dialog.

void __fastcall  TMainForm::HelpContents(TObject *Sender)
{
    char  buff[255];  //local buffer  
    strcpy(buff,_wDir); //copy the director containing the help into buffer  
    strcat(buff,"//help" );//help is stored in a subdirectory called help.
    chdir(buff); //change the directory to help  .
    THelpDialog *help = new THelpDialog("contents.htm" ,this);//show help dialog  
    help->Caption = "Help: Contents" ; // change the caption  
    help->ShowModal();  //show it  
    delete help; //Delete it.  
}
//----------------------------------------------------------------------------

You would like to make help available from other dialogs and forms via the help button. To do this you will need to duplicate the code above in the button pressed handler. The only part of this that will cause trouble is the need to access _wDir. You can do this by adding a public function to the main form that returns this string.

void __fastcall  TMainForm::getHelpDir(char*buff)
{
  strcpy(buff,_wDir);
}

Complete Copy

Click here to get a complete copy of this program.