Channels ▼
RSS

Design

Sympathy on the Loss of One of Your Legs

Source Code Accompanies This Article. Download It Now.


OCT91: STRUCTURED PROGRAMMING

Life is complicated. Art follows life. If greeting cards are indeed art, then either the greeting card industry has gotten awfully complicated, or else all men are Socrates.

An industry that once was happy to declare Happy Birthday! Merry Christmas! Be My Valentine! now has to deal with death, divorce, Halloween, big raises, parole, sick pets, shared custody, and National Secretary's Day. A recent scan down the aisle at a Scottsdale Hallmark shop yielded Happy Anniversary to Mother And Her Husband From Both Of Us as well as Your Father And I Disagree But We Both Still Love You. The diversity is boggling, but even more boggling is the specificity. When a card shop contains 10,000 cards, and when 50 million Americans a day buy at least one card in those shops, the industry can afford to be specific.

Perhaps when you see so much specificity your mind begins to manufacture more of it. Mine does (perhaps as a reaction against our overwhelming bias in favor of generalized code), and that may be why I did a double take halfway down the aisle at the card shop not long ago. A second look at a certain flowery offering showed it to read Sympathy on the Loss of Your Loved One, but damned if I didn't first read it as Sympathy on the Loss of One of Your Legs.

Now that's specific.

The Fallacy of Generality

Ridiculous? Well, wait a minute. What if someone you cared about really did lose one of his or her legs? What card could possibly capture your feelings better than that? (Assuming you were brave enough to express any feelings at all--not everyone would.) My hunch is that such a card would sell perhaps thirty units a year, total--but it would sell to nearly 100 percent of its intended audience, that is, people who lose one of their legs but retain some sense of humor.

And therein lies a lesson.

The major applications sold today (word processing, spreadsheets, databases) are tremendously general in nature. Why? Two reasons: First of all, we who began and carried the microcomputer revolution are pathological tinkerers. We love these damned boxes and we can't stop playing with them. For tinkerers, generality is the necessary condition that allows more tinkering. If you have an application that does only one thing, there's no more tinkering to be done.

Also, in the beginning the installed base for any given machine was small, so to be economically viable, a product had to be warpable to serve many very different needs. When only one dog kennel in a hundred has a computer, you can't make money on the DogMatic Kennel Management System, so you sell the breeders dBase III instead and let them futz up their own systems. On the other hand, once 90 percent of dog kennels have computers, you can create DogMatic and make a handsome living on it.

Both reasons for application generality have their genesis in a time now gone. Many or even most people who buy computers today have no desire at all to tinker. They have their fun playing tennis or sabotaging nuclear power plants. Hacking never once entered their minds as something you do on a Saturday night. They want computers to solve their own very specific problems, not create more problems. They want programs that track tennis scores or manage athletic shoe outlets or spot trends in orange juice futures. General-purpose databases would only infuriate them. People who do not tinker would look upon dBase III as an incomplete product, as something of no earthly use to anyone.

And with an installed base of DOS machines at something like 100,000,000 worldwide, it's getting to the point where the majority of working people are going to have a family computer.

With an installed base like that, you can target the narrowest of vertical markets and still make a living, because nearly everybody in that market will already have a computer. If the market itself exists, it will support a vertical market application.

Go Vertical, Young Man

Of course, there have been vertical market applications as long as there have been computers. The earliest were slanted toward big business categories such as banking, law, and government where margins are high and money flows freely. Little by little, what we're seeing is that all businesses have become vertical markets, because our machines are powerful enough to handle any small business, and cheap enough to be bought by small business without breaking the bank.

A "f' rinstance." I own and operate a programmers' magazine as my "real" job. It's a small business, with only five employees and revenues of considerably less than a million dollars per year. The margins are not high, and we're in no position to pay $50,000 for a minicomputer and $20,000 for a magazine-management package. Fortunately, we don't have to. We bought a vertical-market package for $3500 that runs briskly on a $1300 rotgut 386SX no-name clone box with a monochrome monitor and a 72-Mbyte hard disk. It manages circulation promotion and fulfillment for 30,000 customers, as well as accounting and mailing list management. The package (called QuickFill, from CWC Software) is very slick, very robust, and beautifully documented. The phone support is inexpensive and superb. The vendor is still in business and seems to be doing well. I'd call this a miracle, except that it seems to be a trend.

If there will be a way to make money in this business for the next ten years, this is it: Go vertical. Find a market that isn't saturated, study it closely, and then create an application that is so specific to that market that it requires little or no tinkering on the part of its purchasers. QuickFill followed standard practice in the magazine publishing business so closely that we didn't have to futz it, or change the way we worked to adapt to it. QuickFill is a very nearly totally accurate reflection of the magazine publishing business in the mirror of the desktop DOS machine.

Your mission is to accurately reflect your chosen market in the mirror of the universal DOS machine. If you don't accept it, go back to selling shower curtains.

The Personal Vertical Application

That's the state of things right now, and I'll come back to the issue of small-business vertical market applications shortly. Looking ahead just a year or two, I see another trend unfolding along the same axis: the appearance of the personal vertical market. By "personal," I mean an application that doesn't involve some kind of money-making pursuit. By "vertical," I simply mean specific--highly specific--to a certain type of endeavor, one so specific that no one right now would admit that it could sell enough copies to make money.

Forget the legendary recipe management database. There simply isn't enough management to be done to make such a thing useful. Cookbooks do the job plenty well, and the resolution on the pictures is better. (Who wants to see jaggies on their Spicy Cataluna Chicken Wings?)

No. Let me hand you all an idea I had years ago, but shelved because it was too specific for the installed base at the time. I think its time has come. The idea is hereby declared public domain (lest some lawyer or other moron try to patent it) and may the best implementor win.

I call it Card Shark. It is a graphics drawing program specifically tailored to create greeting cards, and nothing else. It pulls the following elements together:

  1. A large clip-art object library of cute dogs, grotesque caricatures, crosses, flower arrangements, country cottages, or legally licensed characters like Ziggy, Snoopy, or Zippy the Pinhead.
  2. An intelligent "posing" engine that can not only scale such clip art, but also rotate and manipulate it within constraints. Click your mouse and pull Ziggy's pudgy little arm up over his head, waving bye-bye, but still keeping his essential shape within the constraints of the character as Tom Wilson designed him. Put Zippy the Pinhead in various poses until you find one you like. Etc.
  3. A clip-joke collection indexed by occasion. Want an insult related to turning 40? Or one related to getting a big raise? Or an inspirational message encouraging the reader to carry on, even with one leg missing? You don't have to tell the user that the little matrix he or she fills out (factors like raunch quotient, social class, and so on) to go fetch a joke is a query-by-example form.
  4. A bunch of highly versatile fonts in which to cast the card's message.
  5. Laser printable paper stock and envelopes to make the cards real. Forget dot matrix printers. By 1995 they'll seem as quaint as single-sided diskette drives.
  6. A gimmick. For example, a help system implemented as an animated cartoon shark, who pops up on command to answer questions inside of a cartoon speech balloon, or shows up to counsel the user when he or she tries to do something nonsensical. The shark could be just another piece of clip art, with the help system--rather than the user--doing the posing of the character.
Some of this stuff may be slightly state-of-the-art, but only for the next 20 minutes or so. The hardest part will be the posable clip art--keeping in mind that if you do it right, that technology could itself become a product for use in a lot of far better things than silly greeting cards.

I think that many or even most DDJ readers could create a product something like this, especially if you back away from the posing engine a little. Creating the product, you may already be objecting, is the easy part. Where would you sell such a thing?

Don't be silly. You'd sell it in Hallmark card shops. All they do is cards. Do a little market research, create a proposal that explains the concept and why it could work in a nation with 75,000,000 PC compatibles, make the packaging slick and consumer-oriented, and present it to the national Hallmark franchiser. They might click their tongues and perhaps try it first in Santa Clara or San Jose, but after the first few thousand copies flew off the shelves you'd see the thing go national in a big, big way. Consumer products can be like that.

Remember me after you're rich.

Squeezing the Development Cycle

That's only my most ambitious example; I have lots of other ones. As a sometime stamp collector, I would like a highly-specific graphics program that only creates custom stamp album pages. Give me that, and maybe add the ability to link a square on a stamp album page to a database record. But don't make it a stamp-collector's configurable database. Make it a stamp album page generator. I collect stamps to help make me forget programming, remember?

I could fill the rest of this column with suggested personal vertical applications, but by now I hope your own imagination is hard at work, coming up with things totally alien to my own (somewhat limited) experience. Instead of that, let's talk about the prospect for making this stuff pay.

In one sense, Card Shark is a bad example. It's vertical by virtue of being highly specific, but it's also horizontal in the sense that the majority of Americans enjoy and send greeting cards. It will be a challenging product to create, but the potential payoff is high because the potential market numbers in the millions.

Most applications are not that horizontal. And in the near term, at least, the exploding PC installed base in small business presents the more urgent need.

By nature, a vertical market application has a limited market. A given market also has a price point above which the fish won't bite. Markets whose fish bite high have mostly been filled. You'll need to come in cheaper for industries that are not as cash-flush as law or medicine.

Vertical market developers generally know this, or at least sense it: To make an application pay in a limited market, you need to make it happen fast.

And here's a related vertical market developer rule that hobbyists and tinkerers will probably bridle at: Build nothing that you can buy. Building something from scratch that can be had from Programmer's Paradise for $200 without royalties will be at best a stunt and at worst fatal to your project. In this category fall user interfaces, database engines, hardcopy output libraries, and numerous other things. I have serious doubts that you can come up with an unquestionably better user interface toolkit than you can buy or get bundled with a compiler.

Ditto a database engine. QuickFill is inexpensive at least in part because its developers used a database engine called db_Vista (from Raima Inc.) rather than wasting a year or so creating one themselves.

In a nutshell, the key to success in vertical markets is to create as much of the application as humanly possible from standardized, over-the-counter parts. My rule of thumb would be this: Buy at least 75 percent of the application (measured in terms of source code lines) in library form.

I can hear the objection already: What if one of your library vendors goes out of business and you don't have source? Yes, you could be in trouble. But then again, life demands the taking of risks. If you won't take that risk, the developer down the street who does will beat you to market and plow you into the soil.

Learning a Market

Actually, the single most crucial development task in creating a vertical market application is understanding the target market. Compared to that, the programming is almost trivial. Most of your time, funds, and energy, in fact, should go toward making your app an accurate reflection of the industry it serves.

This is mostly not a computer skill. This is mostly a people skill. You may be a true Renaissance man or woman with the ability to grok a market in its fullness after a few days observing it. Probably, you are not. Your best bet in that case is to find a partner who has made his or her life's work in your chosen market, and split ownership of the product and the proceeds 50-50 with that partner.

If this seems to be giving away the store, ask yourself how well you would be able to model a Christmas tree farm in software from your two-flat on the north side of Chicago. The real value-added in a vertical market application is the accuracy of its business model. Your partner is the one who must guide your programming efforts in such a way as to guarantee that accuracy. By contrast, much of what you bring to the equation is the ability to create the model quickly and inexpensively, and to maintain and support it after release without a small army of additional personnel.

It certainly helps if you yourself have some experience in the field you're trying to model, and you certainly can't remain totally ignorant of the field's details. But you still need someone with the ability to see the business from the user's point of view, minus your inherent and inescapable love of tinkering the box for the hell of it.

But once you've chosen your partner, the way to proceed (culled from my numerous conversations with people who do or have done this) is this: Spend a solid month at the actual job site controlled by your partner where the prospective vertical market app will be used. Initiate an ongoing, two-way line of inquiry. You must observe the tasks your partner has to perform to keep the business running, and you must constantly ask why each step is done, and what each step contributes to the larger business context. Take voluminous notes. (A small tape recorder works well during the day, assuming you are willing to spend two hours intelligently filtering its contents to a document file of some sort that night.)

But you should encourage your partner to ask questions of you as well, especially if he or she is nontechnical. Your partner knows the work better than you, and he or she should stop at appropriate points and say, "This is a real logjam operation for us. What can we do with a computer to help streamline the work flow here?" The biggest selling points of your eventual application will be the ways it clears those logjams.

Resist the temptation to redesign the shape of the business to accommodate the program. Today's machines are capacious and fast enough to do things the not-quite-optimal way, if that way more closely mirrors the business being automated. Adhering to the "common practice" prevalent in the business is one of your design constraints. It may even be the most important one.

Do not even begin your design work until you have been soaked to the eyeballs in the everyday work of the business for that full month. Learn the jargon, and incorporate the jargon into the application right at the design level.

"Screen-in" Design

It often makes sense in vertical-market work to design the app from the screen in, by designing screen layouts (or at least approximating them) before any other system elements are decided. The screens come first because they are the places where the user touches the app. The screens should reflect common business practice, and may even be the video realization of business forms in common use in the business. Mimic such forms where possible, especially where a high degree of forms standardization (as in government, tax, or insurance work) exists.

Find a screen design tool that appeals to you -- and teach your partner how to use it. Have your partner take the first cut at designing the screens. You may have to touch them up, but if your partner knows anything about efficient work methods (and if not, the business is probably in trouble), the best screen designs will come from your partner and not from you.

Treat your suite of screen designs as a group of flexible constraints. Once you've got 'em, design to 'em. The screens will probably need to be tweaked as your design shakes out -- but you must try very hard to let the screens shape the design, rather than letting design expediency (read here: programmer laziness) reach back and reshape the screens.

Working Fast

The narrower your market, the faster you had better be able to produce the goods. This means that you have to be good at your chosen language, and that your chosen language must allow the sort of mad-dash gonzo programming (what I call lightning development) you have to accomplish to make money. C is hopeless for lightning development -- but before you C guys start tying the noose, let me throw this additional comment in: Until you've bought or accumulated a truly tremendous high-level toolkit, Pascal isn't a great deal better. Modula-2 is worse than both, because the tools aren't there, and may never be.

While researching this column, I looked at a great many programming environments I had sitting on the shelf, evaluating them strictly in terms of their appropriateness for lightning development. My eventual conclusion surprised me, and it will certainly surprise you: The best environment for lightning development isn't generally considered a language at all (even though it is) and that is Clarion Professional.

Clarion is usually marketed as and reviewed as a relational database manager. There is certainly a relational database beating at Clarion's heart (a damned fine one, too) but that's not what makes it so good. What Clarion has that the Paradox Engine and db_Vista lack is a true structured programming language and superb interactive design tools.

Clarion seems to have been designed specifically to allow lightning development of vertical-market, database-oriented applications that run in text mode. For simple applications, you may not have to actually write any Clarion language code by hand. Using the provided tools, you draw your database schema, draw your user interface and report screens, and let the Clarion engine tie it all up with a bow. Such an application is not very flexible (it basically allows you to enter data into a database and query or report it back again), but it works fast and it's rock-solid -- and you can put it together in an hour-and-a-half once you learn your way around the system.

On more ambitious projects, you rough out an application as far as you can take it with the interactive design tools, and then you bring it the rest of the way by writing code in the Clarion language proper. My friend Kate Daniel is a seasoned Clarion developer who can do in a weekend what a C programmer would proudly brag of accomplishing in "only" ten weeks, and a Pascal guy might do in five.

I've heard gripes that this isn't really programming, sometimes from people who think nothing of wrestling with CASE tools for weeks at a time. Maybe what they mean is that it doesn't hurt enough.

Stepson of Cobol

But I think what bothers them about Clarion is that it looks a lot like Cobol on the surface, primarily because it is traditionally written entirely in uppercase. (Clarion is not case-sensitive and may be written in lower-or mixed-case, as desired.) That, and some old-fashioned control structures such as computed GOTO have been enough to give it a bad name. Clarion, however, is a structured language up with the best of them. Let me give you some specifics.

A Clarion program or module (like a Turbo Pascal unit) has two parts: a declaration part and a code part, separated by the reserved word CODE. All the standard Turbo Pascal data types are supported, plus a 15-digit BCD numeric type. Simple data types may be declared as variables or combined into GROUP structures, which are analogous to Pascal records.

Report specs and screen specs are implemented as data structures as well. This allows the interactive design tools to write the state of a designed report or screen design in such a form as to be directly accessible to the programmer as source code.

A MAP structure may be defined to summarize all declarations used within the current program, including declarations of items defined in other modules. This is roughly analogous to the INTERFACE section of a Pascal unit, and is how modular compilation is expedited in Clarion.

Remarkably, Clarion has a richer suite of flow control structures than Pascal's. In a dition to IF and CASE, Clarion has a very versatile LOOP structure, which can act as either a WHILE..DO loop or a REPEAT..UNTIL loop. Within LOOP, you can use BREAK to break out of the loop before terminating conditions are met, or CYCLE to begin at the top of an iterative loop with the next iterator value. The RESTART statement has no analog in Pascal, but does the job of the setjmp..longjmp pair in C, in that it allows you to set a trap point to which you can return if you find yourself hopelessly mired in a morass of control structures that is caving in on itself. CHAIN allows Basic-style chaining to another Clarion program, and CALL performs a subroutine-style CHAIN to another Clarion program, with the original program's state retained so control can be returned to it. RUN shells out to DOS, either to COMMAND.COM or to some other executable program.

There are two different kinds of procedures: One is the kind we're familiar with in Pascal and C, and the other is a simple GOSUB-style subroutine called a local routine.

And, of course, there are two different kinds of GOTO.

As with any language, you can write spaghetti code in Clarion, but if you can't build clean structured modules with an arsenal like that, you oughta be selling shower curtains, period. If this be Cobol, I'll take a double handful.

Building Applications

The Clarion compiler generates Clarion P-code for a proprietary P-machine. The P-code is generally what is used during development. Once the application is solid, a translator utility converts the P-code to standard DOS .OBJ files that may be linked as a machine-code .EXE.

Clarion has another distinction that is not adequately appreciated: It has among the best documentation sets of any programming language I've ever used. The large three-ring manuals are complete, well-written, virtually indestructible, and lie unconditionally flat on the desk. There's a good tutorial on the interactive design tools, and, most remarkably, an entire book containing annotated example programs written in Clarion. These are not toy programs; one example is 800 lines long. In any new language spec, annotated program examples are absolutely necessary to give you the big picture of program structure once you've become conversant with the individual language statements.

Products Mentioned

Clarion Professional Developer 2.1 Clarion Software 150 East Sample Road Pompano Beach, FL 33064 800-354-5444

db_Vista III Raima Inc. 3245 146th Place SE Bellevue, WA 98007 206-747-5570

With Clarion's permission, I've reproduced a simple mortgage-payment calculator from the examples book in Listing One, page 171. It's necessarily small, but it's quite representative of the spirit of a Clarion program.

More Mortgages

Clarion succeeds because it does not try to be too much. It does not support graphics or event-driven programming, and is best suited for database-central applications that don't get too exotic. On the other hand, you can pick up the essentials of the language in an afternoon and be producing reasonable applications in two or three days. If you have to work fast, there's simply nothing like it anywhere.

Listing Two (page 171) returns us to Pascal-land, actually in preparation for next issue, when we launch into (hold your breath) Turbo Vision. MORTGAGE.PAS is a mortgage object that generates and manipulates a mortgage amortization table. There's nothing the least bit tricky about it. It lacks a user interface, but that's OK...because that's what Turbo Vision does best.


_STRUCTURED PROGRAMMING COLUMN_
by Jeff Duntemann


[LISTING ONE]
<a name="024a_0010">

         TITLE('COMPUTE MONTHLY PAYMENTS')
PAYMENT      PROGRAM
         INCLUDE('\CLARION\STD_KEYS.CLA') !STANDARD KEYCODE EQUATES

SCREEN       SCREEN   HLP('PAYMENT'),HUE(7,0,0)
           ROW(4,25)  PAINT(13,32),HUE(7,1)
         COL(25)  STRING('<201,205{30},187>')
           ROW(5,25)  REPEAT(3);STRING('<186,0{30},186>') .
           ROW(8,25)  STRING('<204,205{30},185>')
           ROW(9,25)  REPEAT(7);STRING('<186,0{30},186>') .
           ROW(16,25) STRING('<200,205{30},188>')
           ROW(6,28)  STRING('CALCULATE MONTHLY PAYMENTS')
           ROW(13,32) STRING('Payment  :')
           ROW(15,30) STRING('Press Ctrl-Esc to Exit')
           ROW(9,32)  STRING('Principal:')
         COL(44)  ENTRY(@N7),USE(AMOUNT),INS,NUM
           ROW(10,32) STRING('Rate {5}:')
         COL(44)  ENTRY(@N7.3),USE(RATE),INS,NUM
           ROW(11,32) STRING('Years    :')
         COL(49)  ENTRY(@N2),USE(YEARS),INS,NUM
PAYMENT        ROW(13,42) STRING(@N9.2)
         .
AMOUNT       DECIMAL(7)          !PRINCIPAL AMOUNT
RATE         DECIMAL(7,3)        !ANNUAL PERCENTAGE RATE
YEARS        DECIMAL(3)          !TERM IN YEARS
MONTHS       DECIMAL(3)          !TERM IN MONTHS
MON_RATE     REAL            !MONTHLY RATE
TEMP         REAL            !INTERMEDIATE VALUE

  CODE                   !START THE CODE SECTION
  ALERT(CTRL_ESC)            !ENABLE THE CTRL-ESC KEY
  HELP('PAYMENT')            !OPEN THE HELP FILE
  OPEN(SCREEN)               !DISPLAY THE SCREEN LAYOUT
  LOOP                   !LOOP THROUGH THE FIELDS
    ACCEPT               !GET A FIELD FROM THE KEYBOARD
    IF KEYCODE() = CTRL_ESC THEN RETURN. !EXIT ON CTRL-ESC

    IF AMOUNT * RATE * YEARS <> 0    !WHEN ALL FIELDS ARE ENTERED:
      MONTHS = YEARS * 12        !COMPUTE MONTHS
      MON_RATE = RATE / 1200         !COMPUTE MONTHLY RATE
      TEMP = 1 / ((1 + MON_RATE) ^ MONTHS)  !COMPUTE MONTHLY PAYMENT
      PAYMENT = AMOUNT * (MON_RATE / (1 - TEMP))
    ELSE                 !OTHERWISE:
      PAYMENT = 0            !  SET MONTHLY PAYMENT TO ZERO
    .                    !END THE IF STATEMENT
    IF FIELD() = ?YEARS          !AFTER THE LAST FIELD
      SELECT(?AMOUNT)            !  SELECT THE FIRST FIELD
  . .                    !END THE IF AND LOOP STATEMENTS





<a name="024a_0011">
<a name="024a_0012">
[LISTING TWO]
<a name="024a_0012">

{ By Jeff Duntemann  --  From DDJ for October 1991 }

UNIT Mortgage;

INTERFACE

TYPE
  Payment = RECORD      { One element in the amort. table. }
              PayPrincipal   : Real;
              PayInterest    : Real;
              PrincipalSoFar : Real;
              InterestSoFar  : Real;
              ExtraPrincipal : Real;
              Balance        : Real;
            END;
  PaymentArray   = ARRAY[1..2] OF Payment;  { Dynamic array! }
  PaymentPointer = ^PaymentArray;

  PMortgage = ^TMortgage;
  TMortgage =
    OBJECT
      Periods        : Integer;  { Number of periods in mortgage    }
      PeriodsPerYear : Integer;  { Number of periods in a year      }
      Principal      : Real;     { Amount of principal in cents     }
      Interest       : Real;     { Percentage of interest per *YEAR*}

      MonthlyPI   : Real;        { Monthly payment in cents         }
      Payments    : PaymentPointer;  { Array holding payments       }
      PaymentSize : LongInt;     { Size in bytes of payments array  }

      CONSTRUCTOR Init(StartPrincipal      : Real;
                       StartInterest       : Real;
                       StartPeriods        : Integer;
                       StartPeriodsPerYear : Integer);
      PROCEDURE SetNewInterestRate(NewRate : Real);
      PROCEDURE Recalc;
      PROCEDURE GetPayment(PaymentNumber   : Integer;
                           VAR ThisPayment : Payment);
      PROCEDURE ApplyExtraPrincipal(PaymentNumber : Integer;
                                    Extra         : Real);
      PROCEDURE RemoveExtraPrincipal(PaymentNumber : Integer);
      DESTRUCTOR  Done;
    END;

IMPLEMENTATION
FUNCTION CalcPayment(Principal,InterestPerPeriod : Real;
                     NumberOfPeriods  : Integer) : Real;
VAR
  Factor : Real;
BEGIN
  Factor := EXP(-NumberOfPeriods * LN(1.0 + InterestPerPeriod));
  CalcPayment := Principal * InterestPerPeriod / (1.0 - Factor)
END;

CONSTRUCTOR TMortgage.Init(StartPrincipal      : Real;
                           StartInterest       : Real;
                           StartPeriods        : Integer;
                           StartPeriodsPerYear : Integer);
VAR
  I : Integer;
  InterestPerPeriod  : Real;
BEGIN
  { Set up all the initial state values: }
  Principal := StartPrincipal;
  Interest  := StartInterest;
  Periods   := StartPeriods;
  PeriodsPerYear := StartPeriodsPerYear;
  { Here we calculate the size that the payment array will occupy. }
  { We retain this because the number of payments may change...and }
  { we'll need to dispose of the array when the object is ditched: }
  PaymentSize := SizeOf(Payment) * Periods;

  { Allocate payment array on the heap: }
  GetMem(Payments,PaymentSize);

  { Initialize extra principal fields of payment array: }
  FOR I := 1 TO Periods DO
    Payments^[I].ExtraPrincipal := 0;
  Recalc;  { Calculate the amortization table }
END;

PROCEDURE TMortgage.SetNewInterestRate(NewRate : Real);
BEGIN
  Interest := NewRate;
  Recalc;
END;

{ This method calculates the amortization table for the mortgage. }
{ The table is stored in the array pointed to by Payments.     }

PROCEDURE TMortgage.Recalc;
VAR
  I : Integer;
  RemainingPrincipal    : Real;
  PaymentCount          : Integer;
  InterestThisPeriod    : Real;
  InterestPerPeriod     : Real;
  HypotheticalPrincipal : Real;
BEGIN
  InterestPerPeriod := Interest/PeriodsPerYear;
  MonthlyPI := CalcPayment(Principal,
                           InterestPerPeriod,
                           Periods);
  { Round the monthly to cents: }
  MonthlyPI := int(MonthlyPI * 100.0 + 0.5) / 100.0;

  { Now generate the amortization table: }
  RemainingPrincipal := Principal;
  PaymentCount := 0;
  FOR I := 1 TO Periods DO
    BEGIN
      Inc(PaymentCount);
      { Calculate the interest this period and round it to cents:  }
      InterestThisPeriod :=
        Int((RemainingPrincipal * InterestPerPeriod) * 100 + 0.5) / 100.0;
      { Store values into payments array: }
      WITH Payments^[PaymentCount] DO
        BEGIN
          IF RemainingPrincipal = 0 THEN  { Loan's been paid off! }
            BEGIN
              PayInterest := 0;
              PayPrincipal := 0;
              Balance := 0;
            END
          ELSE
            BEGIN
              HypotheticalPrincipal :=
              MonthlyPI - InterestThisPeriod + ExtraPrincipal;
              IF HypotheticalPrincipal > RemainingPrincipal THEN
                PayPrincipal := RemainingPrincipal
              ELSE
                PayPrincipal := HypotheticalPrincipal;
              PayInterest  := InterestThisPeriod;
              RemainingPrincipal :=
                RemainingPrincipal - PayPrincipal; { Update running balance }
              Balance := RemainingPrincipal;
            END;
          { Update the cumulative interest and principal fields: }
          IF PaymentCount = 1 THEN
            BEGIN
              PrincipalSoFar := PayPrincipal;
              InterestSoFar  := PayInterest;
            END
          ELSE
            BEGIN
              PrincipalSoFar :=
                Payments^[PaymentCount-1].PrincipalSoFar + PayPrincipal;
              InterestSoFar  :=
                Payments^[PaymentCount-1].InterestSoFar + PayInterest;
            END;
        END;  { WITH }
    END;      { FOR }
END;          { TMortgage.Recalc }

PROCEDURE TMortgage.GetPayment(PaymentNumber   : Integer;
                               VAR ThisPayment : Payment);
BEGIN
  ThisPayment := Payments^[PaymentNumber];
END;

PROCEDURE TMortgage.ApplyExtraPrincipal(PaymentNumber : Integer;
                                        Extra         : Real);
BEGIN
  Payments^[PaymentNumber].ExtraPrincipal := Extra;
  Recalc;
END;

PROCEDURE TMortgage.RemoveExtraPrincipal(PaymentNumber : Integer);
BEGIN
  Payments^[PaymentNumber].ExtraPrincipal := 0.0;
  Recalc;
END;

DESTRUCTOR TMortgage.Done;
BEGIN
  FreeMem(Payments,PaymentSize);
END;

END.  { MORTGAGE }


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video