Re: Writing reusable 4GL
Posted in 1995
In article <3smtks$2rj@cssun.mathcs.emory.edu>,
huddles@emspo01.hasting.com ("Huddleston, Joe") wrote:
> i'm sorry...i must have missed your original message. could you repost it?
It would seem that the original didn't get out to the rest of the world
(or at least that's what lots of email tells me), so here's another crack:
----------------------------- REPOST ------------------------------------
I've been wanting to write this document for some time. I hope it will
inspire some 4GL programmers to look at modular variables, and see how
fantastic they can be for code reuse.
A reasonable introduction to classes too, for those on an OOPL path...
(although I think I may have got some of the OO jargon wrong)
BTW: I'm keen to get a debate going if there are people out there who
disagree with my enthusiastic rantings.
Regards,
Kerry S
------------------------------------,------------------------------------------
Kerry Sainsbury, kerry@kcbbs.gen.nz | THE INFORMIX FAQ v2.2 Mar 95
Quanta Systems, Auckland | kcbbs.gen.nz:/informix/informix.[faq|apx]
New Zealand. Work: +64 9 377-4473 | mathcs.emory.edu:/pub/informix/faq/ " "
Home: +64 9 279-3571 | http://www.garpac.com/informix.html
-------------------------------------------------------------------------------
WRITING REUSEABLE INFORMIX-4GL CODE
(or: Why I love Modular Variables)
Kerry Sainsbury, July 1995
I want to explain how to create easily reuseable and extendable software
components. Object Oriented Programmers use some of these buzzwords, we can
too if you like:
Encapsulation: All code related to a task lives in a single place, with the
actual implementation hidden from the user code.
Inheritance: The ability to take existing functionality and extend it
without breaking existing user code.
ENCAPSULATION
Imagine we need a program to control a simple vending machine :-) Each
machine needs to be able to:
- accept money
- accept product selection
- deliver product
- give change
- record the sale
We will need to write a different program for each product manufacturer to
cope with their own special requirements, but these are the essential
functions.
Here are some solutions we could adopt:
A. The MAIN way.
We could code the entire routine in one MAIN section, without any
functions, and use local variables to keep track of data.
main_eat.4gl:
MAIN
DEFINE l_money, l_product, l_cost, l_change...
DISPLAY "EAT FOOD" -- Special code for this vendor
INPUT l_money...
INPUT l_product ...
DISPLAY "Here is your ", l_product
SELECT l_cost FROM price WHERE product = l_product
LET l_change = l_money - l_cost
DISPLAY "Here is your change: ", l_change
INSERT INTO sales(product, day) VALUES (l_product, TODAY)
DISPLAY "THANKS FOR EATING FOOD" -- More vendor-specific code
END MAIN
This is a poor solution because the only way we can reuse this code (for
example in a later model machine with the ability to disallow selection
of out-of-stock product, or the ability to vend coffee) is to cut and
paste the code into a new program.
Similarly if we later wish to change the recording of sales to include
TIME of purchase we will need to remember to alter both the original
vending program, that of the later model, and that of the coffee
machine.
B. The GLOBAL way.
We could code each routine individually and communicate between the
routines via global variables.
main_drink.4gl:
GLOBALS g_money
MAIN
DISPLAY "DRINK SUGAR" -- Vendor specific
CALL enter_money()
DISPLAY "Thanks for the $", g_money -- Vendor specific
CALL enter_product()
CALL deliver_product()
CALL give_change()
CALL record_sale()
END MAIN
vend.4gl:
GLOBALS g_money, g_product, g_cost, g_change...
FUNCTION enter_money()
INPUT g_money...
END FUNCTION
FUNCTION enter_product()
INPUT g_product ...
END FUNCTION
FUNCTION deliver_product()
DISPLAY "Here is your ", g_product
END FUNCTION
FUNCTION give_change()
SELECT g_cost FROM price WHERE product = g_product
LET g_change = g_money - g_cost
DISPLAY "Here is your change: ", g_change
END FUNCTION
FUNCTION record_sale()
INSERT INTO sales(product, day) VALUES (g_product, TODAY)
END FUNCTION
This looks like a reasonable solution except that a maintenance
programmer has no idea what function sets "g_money" on the vendor-
specific line in MAIN, and sharing global variables between programs is
a VERY messy business.
The programmer also needs to somehow know that "g_money" is the global
variable he needs to use, and hope that "g_money" doesn't already exist
in his program when he finds he needs to add an interface to vend.4gl
(remember in the real world it's unlikely to be something as clear-cut
as a vending machine!)
C. The LOCAL way.
We could code each routine individually and communicate between the
routines via local variables.
main_chocolate.4gl:
MAIN
DEFINE l_money, l_product, l_money, l_cost, l_change
DISPLAY "EAT CHOCOLATE" -- Vendor specific
LET l_money = enter_money()
DISPLAY "Thanks for the $", l_money -- Vendor specific
LET l_product = enter_product()
CALL deliver_product(l_product)
CALL give_change(l_product, l_money)
RETURNING l_cost, l_change
CALL record_sale(l_product)
END MAIN
vend.4gl:
FUNCTION enter_money()
DEFINE l_money
INPUT l_money...
RETURN l_money
END FUNCTION
FUNCTION enter_product()
DEFINE l_product
INPUT l_product ...
RETURN l_product
END FUNCTION
FUNCTION deliver_product(l_product)
DEFINE l_product
DISPLAY "Here is your ", l_product
END FUNCTION
FUNCTION give_change(l_product, l_money)
DEFINE l_product, l_money, l_cost, l_change
SELECT l_cost FROM price WHERE product = l_product
LET l_change = l_money - l_cost
DISPLAY "Here is your change: ",