An organizational approach to rapid application
development
By Roy E. Bourquin
This article is about creating applications from object hierarchy code instead of starting each app with TForm1.
Suppose the CFO comes to us and tells us
that the company needs an application for the accounting department to
automate the transaction process from the point of capital entry
to the point of liability disbursement. Additionally, the CFO
needs to have it in place two months before the quarterly report
is due.
No problem! We simply launch our favorite
Borland RAD tool. Now create an MDI form. Add the TClientSocket,
TMainMenu, TToolBar, TSpeedButton, TStatusBar, TStringGrid,
TQuery and a few TForm components for our child windows. Then
fill in the blanks and shazam, we're ready to deploy
the application to the accounting department.
Now that our application was completed on
time and on budget we received the attention of the CEO, who requests that we create an application that automates the
reporting of the assumption, cash flow, balance sheet, and income
statements. The CEO needs to have it in place one
month before the quarterly report is due.
No sweat. We sizzle up another
application in the same fashion. This time we want to make a
really good impression. So we perform the same routine as before,
only this time we add TQuickRep for printing the reports and a
web component to publish the information to the corporate Web
site.
Now that we've automated the quarterly
reporting requirement we're ready to relax and let our
application do its thing. Then the Audit Department sees the
quarterly reports online and requests that we build another
reporting tool for auditing the information in our automated
system.
So we start again...
RAPID APPLICATION DEVELOPMENT THAT IS TOO
RAPID
Lets recap what we have done so
far. When we bring up the IDE, the first thing that we see is a
fresh form. Then we add components from the component
palette. Additionally, to make things a little easier we added a
few "helper" functions. These functions include a few
financial, file I/O, and component manipulation members.
One of the functions that we added was a
SetColumnName() function that would allow us to set column names
for our StringGrid. Snippet 1.1 shows how we implemented this.
Notice that our function belongs to the TForm1 class. This makes
it a little hard for us to use this function again.
SNIPPET 1.1
void TForm1::SetColumnName(int li_Column, const AnsiString ls_Name)
{
ColCheck(li_Column);
StringGrid1->Cells[li_Column][0] = ls_Name;
}
STARTING WITH THE OBJECT HIERARCHY
Since we are the customers of our own work,
we will always try to build our software as if it were an SDK. To
do this we will start by creating an object hierarchy. I suppose
we could call our base class IUnknown, TObject, root or something
like that. For now we'll choose MyBaseClass.
The classes that will suit our needs are
displayed in Hierarchy Diagram 2.1, which shows some of the basic
functionality that our application will need. We can always add
more object as our needs grow. Then we'll simply inherit from the
appropriate base class.
After designing our objects, we start
adding member prototypes. We'll make all our company proprietary
code private members and wrap them up with appropriate public and
protected members. And of course we document what each member is
used for. Then we add the members. For now we just put a remark
in each member with the TODO keyword. That way we can use the
VIEW | TODO feature in our IDE and tackle the algorithm later.
This will give us an opportunity to prioritize the members based
on the project deadlines.
HIERARCHY DIAGRAM 2.1

We would like to reinvent the wheel as much
as possible, but since other venders have been more successful
and distributing their operating systems than we have, we'll have
to integrate our hierarchy with a platform that won't break with
the next release of the operating system. The platform that we
chose here was Object Pascal, since it has the crossplatform
compatibility for multiple operating systems (namely 95/98/ME/NT
and now Linux). Our new integrated hierarchy is displayed in
Hierarchy Diagram 2.2.
HEIRARCHY DIAGRAM 2.2

Let's take a look at the implementation of
our financial class. Since we want to use those nifty Object
Inspector features, we'll add a new component and inherit from
TComponent. Snippet 2.1 shows our implementation of the prototype
declaration.
SNIPPET 2.1
class PACKAGE TFinancial : public TComponent
{
private:
double FLoanAmount;
double FInterest;
double FYears;
AnsiString FMonthlyPayment;
void __fastcall mpSetText(const AnsiString Value);
AnsiString __fastcall mpGetText();
protected:
public:
__fastcall TFinancial(TComponent* Owner);
void __fastcall Amortize();
__property AnsiString MonthlyPayment = {read = mpGetText, write=mpSetText};
__published:
__property double LoanAmount = {read = FLoanAmount, write=FLoanAmount};
__property double Interest = {read = FInterest, write=FInterest};
__property double Years = {read = FYears, write=FYears};
};
Next we'll complete the algorithm for our
financial members. The implementation is given in Snippet 2.2.
SNIPPET 2.2
void __fastcall TFinancial::Amortize()
{
bool lb_zero = false;
if (FLoanAmount <= 0) lb_zero = true;
if (FInterest <= 0) lb_zero = true;
if (FYears <= 0) lb_zero = true;
if (lb_zero) return;
FInterest = FInterest/100;
FYears = FYears*12;
MonthlyPayment = FormatFloat("$0.00", FLoanAmount/((1-(1/(pow((1+(FInterest/12)),FYears))))/(FInterest/12)));
}
void __fastcall TFinancial::mpSetText(const AnsiString Value)
{
FMonthlyPayment = Value;
}
AnsiString __fastcall TFinancial::mpGetText()
{
return FMonthlyPayment;
}
Now let's revisit our work from Snippet 1.1.
This time we inherited from TStringGrid so that our
SetColumnName() member belongs to our new StringGrid object
instead of our TForm class. Snippet 2.3 shows the prototype
declaration and new StringGrid members. Since we can't perform
multiple inheritance and we want our financial functions to be
available to our new StringGrid, we've implemented the financial
object.
SNIPPET 2.3
class PACKAGE TNewStringGrid : public TStringGrid
{
private:
int GetColumn(AnsiString ls_Name);
void ColCheck(int li_Column);
void RowCheck(int li_Row);
protected:
public:
TComboBox *GridDropDown;
TFinancial *Financial;
void SetColumnName(int li_Column, const AnsiString ls_Name);
void UpdateCell(int li_Row, const AnsiString Name, const AnsiString ls_Value);
void UpdateCell(int li_Row, const AnsiString Name, int li_Value);
__fastcall TNewStringGrid(TComponent* Owner);
void __fastcall EV_Click(TObject *Sender);
__published:
};
void TNewStringGrid::SetColumnName(int li_Column, const AnsiString ls_Name)
{
ColCheck(li_Column);
Cells[li_Column][0] = ls_Name;
}
void TNewStringGrid::UpdateCell(int li_Row, const AnsiString ls_Name, const AnsiString ls_Value)
{
RowCheck(li_Row);
Cells[GetColumn(ls_Name)][li_Row] = ls_Value;
}
void TNewStringGrid::UpdateCell(int li_Row, const AnsiString ls_Name, int li_Value)
{
RowCheck(li_Row);
Cells[GetColumn(ls_Name)][li_Row] = IntToStr(li_Value);
}
int TNewStringGrid::GetColumn(AnsiString ls_Name)
{
int li_Column;
for (li_Column = 0; li_Column < ColCount; li_Column++)
if (Cells[li_Column][0] == ls_Name) return li_Column;
return -1; //Perhaps Throw Exception instead.
}
void TNewStringGrid::ColCheck(int li_Column)
{
if (li_Column > ColCount) ColCount = li_Column;
}
void TNewStringGrid::RowCheck(int li_Row)
{
if (li_Row > RowCount) RowCount = li_Row;
}
Now bring on the next project. Let's just
hope that it needs a StringGrid that performs amortization
calculations.
SUMMARY
We started by looking at what happens when
we build applications using the TForm component as our building
block. Since we created some useful functions that we wanted to
implement in other projects, we decided to start with an object
hierarchy and determined the type of objects that we would need.
Then we added members to each object. We found other useful
objects that were already complete and integrated them into our
hierarchy. The steps for our development cycle are given below:
- Start by creating a generic object
hierarchy for the project.
- Determine the types of components that will be used.
- Inherit from those objects and build classes with the
additional functionality and wrapper functions that will be used
as prototypes.
- Fill in the todo comments for each member (that way, we can
delegate them).
- Prioritize the member functions from the todo list in order of
importance.
- Test/QA each member upon completion and check them off the
todo list.
- Put the project together.
Who knows, maybe one day in
the future we'll be able to work directly from an object
hierarchy RAD hybrid. We'll be able to drag and drop objects,
which will automatically create classes. We'll be able to connect
the objects to perform inheritance. Then we will be able to add
members to each object and check a box like published, virtual,
int, etc., which will automatically generate the text for us.
Then we will add comments, history and algorithms. This will
allow us to print out documentation and collaborate through the
network with our peers, all through the IDE. It will be just like
the good ol' days of telnet and keep it small and simple, and
we'll be able to work from our home office again. No more
spaghetti and meatballs. (I should talk!) All we need is a huge
monitor.