× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.



Joe,

There are several points you fail to see and understand:


1. I agree, many more complex transactional systems involve custom logic
such as price calculations, WIP, MRP, etc.  Nothing new or scary here, this
is standard application development from my perspective which can be easily
handled within a framework as I'll describe, first we need to examine how
you do your "complex calculations" because they themselves heavily are based
on reoccurring patterns.
2. Most application development tasks, calculations, whatever you want to
call it, breaks down into more fundamental subprocesses.  These most basic
processes are:
a. Getting data by various criteria  (query / read, set lower limit and
chain, etc )
b. Updating data  (with complex validation and computations at times)
c. Deleting data
d. Inserting or adding data
e. Processing and decisions and calculations before and after all of the
steps above.  To make your "complex calculations" you normally are getting
data from the current customer, their past orders, their status, their
default currency, etc.  After gathering the decision criteria, you apply
some math or logic or business rules and then probably write some
"calculation" or result out to your database.  They only thing custom in
this whole process is doing the calculation or business rule.
3. Most applications and development projects spend the MAJORITY of their
time and resource doing reoccurring tasks and a smaller percentage doing
custom logic BECAUSE custom logic usually involves using these reoccurring
tasks.  Applications vary but normally this holds true.

Let me provide a concrete example and some code snipits to further
illustrate.

Business Scenario:  Wabash National, one of largest transportation
manufacturer's of tractor trailers in the world, had a fax and MS Access
based solution to manage their warranty process.  Wabash provides trailers
to Fed Ex, JB Hunt, Schneider, Swift, etc.  The fax and MS access system
could not keep pace with the 10's of thousands of warranty claims made each
year.  We developed a system for them using 100% Java working against their
backend DB2 files.  This system is used by hundreds of service centers and
part vendors as well as Wabash warranty department.  Processing could be
complex but fit perfectly in an OO model.  I could provide dozen examples
but let me simplify with just a couple.  When a claim was submitted by a
service center (responsible for making a Warranty repair) we had to compute
the claim total.  The claim amount was based on a)  Who is the service
center and where are they located.  Service centers could be Canadian or
Mexican or American, all claim amounts need to be in the currency of the
service center and you need to know the current currency conversion which
JDE files provided  b) What parts where used on the claim  c)  Are these
parts taxable  d)  Where these supplied by Wabash or the service center; we
only pay if we had to supply the parts  e)  What is the amount of labor in
hours, and what does the service center charge per hour, etc.

Here are code snipits that processed those business rules.  The basic
process still reduced down to the reoccurring patterns I described above
with some custom logic:

Major Objects/Classes:

Row - Represents an actual data record
Field - Represents a column within a Row such as a SSN, ZipCode, PO#, etc
RowCollection - A collection or Rows returned from a query request


Sequence:
a) Service Center is about to submit a claim,  framework or pattern
automatically invokes the "preInsert" method on the ServiceCenterRow
(subclass of ClaimRow which subclasses base Row).
b) ClaimRow overrides the preInsert method which gives any and all objects a
chance to handle "complex processing" before an insert occurs

/**
 * Handle Claims submits considering if the repair has already been made
 *
 */
public void preInsert(ExecutingContext pContext) throws CMException {


        // Check what process we are performing
        String mrKey = (String) pContext.getObject(WarrantyRow.MAGIC_KEY);
        if (mrKey == null) {
                //throw new CMException("Internal Error - 
ServiceCenterRow.preUpdate could
not get key from request.");
        }

      // The repair has already been completed, so handle it
        if (MAGIC_REPAIR_COMPLETE.equals(mrKey)) {
                prepareForRepairComplete(pContext);
        }
        // The repair is submitted for approval
        else if (MAGIC_SUBMIT_TO_WNC.equals(mrKey)) {
                prepareForSubmitToWNC(pContext);
        }
}

The claim is being submitted:  preInsert calls this method to prepare a Row
for submittal

/**
 *  Handle processing for a claim about to be submitted
 */
public void prepareForSubmitToWNC(ExecutingContext pEc) throws CMException {

        // check if claim has total greater than zero
        double total = getValueAsDouble(CLAIM_TOTAL_AMOUNT);

     // Claims with a zero amount are not allowed , throw an exception which
the framework will automatically propagate to the UI
        if (total <= 0 && !hasWNCSuppliedPart()) {
                throw new ValidationException("Invalid claim total.", "Claim 
being
submitted to WNC must have a claim total greater than $0.00.");
        }
        // set the service center status of the claim to WNC pending

                setFieldValue(STATUS, STATUS_WNC_PENDING);

        // set the WNC status of the claim to pending

setFieldValue(WNCSTATUS, STATUS_WNC_PENDING);

      // Based on the trailer VIN, go to the master trailer table, get and
automatically copy Trailer information into this ClaimRow
      // Information copied includes Manufacturer, year built, manufacture
line, model, year sold, year put in service
        Field vin = getField(VIN);
        if (vin != null) {
                RowCollection vinInfo = getVinInfo();  // Method gets the VIN 
record
                // we are only going to take the VIN info from the first row 
that was
returned.
                if (vinInfo != null && vinInfo.size() == 1) {
                        Row row = vinInfo.getRow(0);
                        // Generic method to copy from Row to Row based on 
common field names.
                        this.copyIntoRowFieldsWithCommonFieldNames(row, true);
                }
        }
}

c) Framework goes on to call ServiceCenterRow.validate where both generate
and custom validation can occur.  During the insert process, the currency
rate has to be determined depending on the service center making the Claim.
(Canadian, USA, etc) Claim total is adjusted per currency before insert
occurs

/**
 *  Return the currency conversion rate used to determine the total claim
amount
 */
public BigDecimal getCurrencyExchangeRate() throws CMException {

        try {
                BigDecimal currency;

                // Check if the currency rate has already been set on this claim
                Field er = getField(CURRENCY_EXCHANGE_RATE);
                Field cc = null;

                int status = getStatus();

                if (status == IClaimmastTable.STATUS_DRAFT) {

                        // if currency is not set then try to get it from 
contactbu
                        int buid = getServiceCenterBuId();
                        if (buid == -1) {
                                return one();  // default to USA
                        }
                        // Get the actual service center Row based on this 
contact id
                        ContactBURow contact = getContactBu(buid);

                        // !!C MT (8/28/2003 10:37:20 AM) - There should always 
be a contact bu
associated with a claim
                        if (contact == null) {
                                if (isLegacy()) {
                                        return one();
                                }

                                throw new CMException("Internal error. 
ClaimRow.getCurrencyExchangeRate
could not retrieve information for the contact bu</H4>");
                        }

                        // Get the country currency for this service center

                        cc = 
contact.getField(IContactBuTable.COUNTRY_CURRENCY_CODE);
                   // default to 1 if not specified
                        if (cc == null || cc.getValueAsString() == null) {
                                return one();
                        }
                   // based on the currency code, go get the current
exchange rate for today,  uses JDE conversion tables
                        currency = 
getCurrencyExchangeRate(cc.getValueAsString());

                        // set currency on row
                        er.setValue(currency);
                }

                return ((BigDecimalField) er).getValueAsBigDecimal();
        }
        catch (Exception ex) {
                throw new CMException(ex, "ClaimRow.getCurrencyExchangeRate");
        }
}

D) Insert of a claim occurs.




4)  So I'm able to add custom code and logic of unlimited complexity using
EITHER procedural or OO techniques.  The real benefit is that I only have to
add the custom code at predefined framework points.  Predefined extension
points are are numerous such as:  preInsert, postInsert, preUpdate,
validate, initialize, etc....      Where ever the framework is at work, I'm
able to jump in and handle any special cases I need to and ONLY IF I NEED
TO.


<Joe>

Paul, there is no easy way to do this, so I'm going to be blunt.  The
reason I didn't respond to your last post was because you seemed to miss
the point of my post where I separated business programming into file
maintenance, executive inquiries and transaction processing.

The vast majority of your patterns handle only my first category of
applications, essentially master file maintenance.  No matter how
complex, file maintenance is file maintenance.  You've written one Work
With program, you've written them all.  And OO probably can be made to
handle that portion of the load just fine, provided one is willing to
back away from JDBC and provide a real security layer.

</Joe>

In your procedural case and approach you are writing redundant code for
occurrence of the pattern versus once per pattern.  How would you handle
special logic and processing with the procedural approach in the case where
you may be maintaining Claims or Invoices or Customers and depending on the
type you are updating need to incorporate special logic?  For example,
before updating an invoice it needs a valid PO, before updating a customer
it needs to verify credit limits.

With OO and frameworks, you simply override the "preUpdate" method adding
any logic needed.  The frameworks calls you and you can veto the update if
you want to.  Procedural is reduced to monolithic "if then else" code again
handling each update process case by case.

So I disagree with you here, you can copy or generate some common "work with
" utility but is will not be flexible and extendible and will not meet the
business requirements at least that I'm faced with.

<Joe>

Basically, you're talking about the very first layer of business
programming, that of entering and validating data records.  I'm not
trying to minimize the issue; what you're talking about is certainly an
important part of any business application.

But it isn't what I talk about when I talk about business applications.
I'm talking about the hard stuff, which I've expressed over and over
again: pricing calculations, finite forward scheduling, batch balancing,
MRP generations, WIP calculations, standard costing, all of the stuff
that doesn't come from the database, the stuff that differs from client
to client, from customer to customer, from day to day.  It is this level
of programming that I have yet to see anyone properly design an OO
framework for.

</Joe>

I'm talking about all levels of programming.  I've effectively used these
techniques on applications of all size and they work.  The framework handles
common processing, you tack on your custom processing.  This is the
industrial revolution for software.  The ability to pump out mass
application with the easy ability to customize where needed.  If you would
like, I can show you overridden code of any complexity.  The process is
gather your data, make your decisions, return your result to the framework.
If no complex decisions need to be made, I do nothing and the standard
framework processing handles everything needed.

<Joe>
In business application programming, issues like subfile paging and
field validation are secondary.  It is assumed that all the critical
information has been parsed and validated.  Now it is time to actually
PROCESS that information, and it is this level of programming where OO
is less than satisfactory, in terms of productivity, performance and
flexibility.  I am convinced that there is no way you can program an MRP
generation process in Java in the time I can do so in RPG.  And after
you're done, I am confident that I can throw some real world wrinkles
into the business requirements that will take you far longer to
implement in your OO environment than it would take with my procedural
programs.

</Joe>

If you truly analyze the time you spend developing an application you will
find that the majority is spent on recording task as I've explained.  Your
custom logic of pricing, MRP generations, etc is probably a distant second
in terms of time spent. As you mentioned, to do those calculations, you are
getting data, etc.  The framework can get, cache, query, update, whatever
you need without effort.   Therefore I know solving these reoccurring
problems would yield far more benefit to your productivity than being
concerned weather Java can handle BigDecimal precision, and if then else
logic.

RPG would probably win in a performance race depending on the situation.
Again, I'm not anti procedural nor anti RPG.  Leverage them where you can.

Can you explain how the procedural approach would handle paging/subfile
processing from file to file along with the rest of the patterns I've
described?

Applications of any level that you describe can be abstracted into these
patterns and done effectively with OO.

So with all due respect, as I've said before if you are challenging me in
any of the three application areas you mentioned, I'm ready.  You should
also understand the majority of Web based applications being developed today
ARE of a maintenance, or EIS inquiry in nature.  To a much lesser extent,
large projects in Java are occurring.  We've started a GL rewrite in Java a
few weeks ago.   Again, I make my living solving today's business needs and
they are as I mentioned.

<Joe>
It is simply a matter of the nature of the language.  While you are busy
trying to determine the object hierarchy of material requirements and
identifying the interface for the factory class that converts order
entry detail lines into soft allocations, I'm already writing the
calculations and working on the detail, because I can use those records
as is.  By being as close to the database as RPG is, I don't have to go
through the intermediate layer of defining objects and their attributes.
Your only hope to even stay with me is to simply execute raw SQL
statements in your factory to directly derive the objects from the
database and then figure out some way to order them in collections.
With million-record MRP generations, that's no small feat, by the way.
And by the time you've done that, I'm already processing the results
(not to mention the fact that mine probably runs a lot faster).
</Joe>

At any time, I can drop down to writing procedural code very similar to what
you mention.  Subclassing and inheritance are only used if they provide a
benefit.  It is not difficult to have a routine to go to the raw data as you
mention.  Here is code for a client that notifies the buyer via email when a
supplier changes a purchase order.  Again, a simple override of standard
methods.  Uses configurable property to make business decisions.  This
method is called by the postUpdate() via the framework.

/**
 * Vendor has changed the PO!  Send an email to Buyer
 */
public void emailBuyer(ArrayList changes) throws CMException {
        ExecutingContext ec = ExecutingContext.getCurrentExecutingContext();
        boolean isWarn = cat.isEnabledFor(Priority.WARN);

        // Get the configured SMTP server which is simply a field in a standard
config DB, generic method to get config properies
        String smtp = (String)
getConfigProperty(IEmailObject.CONFIG_KEY_SMTP_SERVER, ec);
        if (smtp == null) {
                if (isWarn) {
                        cat.warn("No SMTP server configuration property exists 
for owner.
Therefore no email can be sent.");
                }
        }

        // need to retrieve the Buyer's email from the Buyer authentication file
        SQLContext context = new SQLContext(getSystemAlias());
        context.setCacheResults(false);  // no need to cache this data
        String library = getConfigProperty("DATA_LIBRARY", ec).toString();  //
library and file can changed based on division
        String buyerTable = getConfigProperty("BUYER_TABLE", ec).toString();
        String sql = "select BMEML from " + library + "." + buyerTable + " where
BMBUY = '" + getBuyer() + "'";
        context.setSQL(sql);

        // get the email Row using a Factory getter
        RowCollection rc = DataEngine.getRows(context);

        if (rc.size() != 1) {
                throw new CMException("Unable to determine a recipient for the 
email.");
        }
       // Find the email field within this Row
        String email =
rc.getRow(0).getFieldWithUsageId(EMAIL_FIELD).getValueAsString();

        if (email == null) {
                        cat.warn("No email address exists to send an email to.  
Therefore no
email can be sent.");
                }
        }

        String body = constructBody(changes, System.currentTimeMillis(),
getVendorNumber().intValue(), getPO().intValue(), getLineNo().intValue());
//getEmailBody(ec);
        List to = Arrays.asList(new String[] { email });  // who is the mail 
going
to?
        List replyTo = Arrays.asList(new String[] { "suppliernet@xxxxxxxxxxxxx" 
});
// hardcoded, perhaps should be a config property
        CMMailer mailer = CMMailer.singleton().createMailer("", "", smtp);
        // create message
        CMMailMessage mail = new CMMailMessage();
        mail.setContentType("text/html");
        // send from
        mail.setFrom("suppliernet@xxxxxxxxxxxxx");
        mail.setAuthenticate(false);

        // set subject
        mail.setSubject("SupplierNet: PO:" + getPO() + " Line#: " + getLineNo() 
+ "
changed by Vendor.");
        // set who sent to
        mail.setTo(to);
        // reply to
        mail.setReplyTo(replyTo);
      mail.setBody(body);
        mailer.sendMessage(mail);


<Joe>
In a procedural language, such programming requires an intimate
knowledge of the database, a detailed understanding of the business
requirements, and some really solid programming skills.  It's a
completely different process than creating yet another "Select and
Display" program.

</Joe>

In OO, we have to fully understand the requirements as well.  The Select and
Display pattern actually involves distinct sub patterns consisting of
"select records" and return them to me while "Display" involves forwarding a
group of records (RowCollection) to a display engine.  If I want to get
records I use the select as shown above.


<Joe>

I could go through a similar analysis of the pricing issue.  Here, the
area where the system breaks down is the sheer number of possibilities.
You end up with either an unwieldy and brittle hierarchy, or else you
devolve into writing procedural code inside of your objects.  And once/Joe
you've done that, to me you've basically just accepted the worst of both
worlds.

Paul, I don't discredit your abilities, your credentials, your
experience, or your accomplishments.  I think you've done a terrific job
on one segment of the business application spectrum.  I'm simply
restating my opinion that in situations where non-repetitive programming
is required, OO is less flexible and less productive in meeting
day-to-day business requirements than procedural languages and
particularly RPG.

</Joe>

I disagree again.  Frameworks can handle the routine work, you add you
custom logic where and when needed.
To produce business applications of any type, you need to incorporate these
common reoccurring requirements.  How many applications (even your MRP) that
you write don't involve selecting records, updating, displaying, processing,
validating, inserting, etc?

As I mentioned as a friendly educational and learning project, if you want
to challenge your approach against what OO can provide, I'm ready.
(Actually I'll be at a client next week so I'm ready this week  :^)


Joe

Respectfully,  Paul Holm


As an Amazon Associate we earn from qualifying purchases.

This thread ...

Follow-Ups:

Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.