|
Buck Calabro <mcalabro@commsoft.net> wrote: > > I see what you're saying. As usual, I said half of what I should > have, and > twice as much as I needed to. When I say "intent" I mean to say tha > the > programmer intended to solve some business problem. The more the co > reveals the chosen solution to that business problem, the clearer th > "intent." Historically, the closer we get to the hardware/operating > system, > the more obscured the solution becomes. Imaging how long it would > take to > decipher the assembler instructions used by a simple PC application > like > EDLIN. This is what makes abstraction such a powerful tool for > writing > programs and solving business problems. I'm not concerned with whic > register I need to store values in, or how many bytes I can read int > the > I/O buffer before it overflows, or what columns does a field occupy > a > record. Believe me, Buck, you're preaching to the choir! All of my programs are modular in design. I usually take the approach that I'm "creating my own extensions to the language" for each application that I write. My general approach to the design of the program is to figure out what lower-level reusable routines I would need to make a program that's very easy to read, yet clearly illustrates what my program does and how it does it. I then, based on this design create a high-level program, which in simple cases consists of only a mainline -- or in more complicated cases may consist of more. And finally, I create procedures to handle the lower level stuff. This approach allows me to write programs very quickly, because it forces me to think of everything that I'm going to need to do at the outset, and create an outline (in code) for how my program will function at the outside, while still allowing me a great deal of reusability in my code. > > >Our shop uses naming standards that make it immediately > >obvious which fields come from which files. These standards > >make it impossible for fields in one file to have the same > >name as fields in another file. > > Great! I only wish that more shops had similar standards (any > standards?!) Our shops standards save me HOURS every day. Literally takes hours off of the workload that I would otherwise have, and saves my company having to pay more people. It does this by making it immediately obvious what a variable or field is, where it comes from and where it is likely to go. This saves me having to search up and down through all of the code figuring out what something is... and makes it very easy to see why code does what it does. I am fortunate to be in a position where I can enforce that standards are followed by the other people around me. This hasn't always been the case however -- when I have to deal with older code that didn't follow the standards, I really realize how useful those standards are. > >With the EXCEPT approach, you now have to go down to > >the Output specs to see whats affected, so for us, it would > >make things more difficult to follow. > > This is an interesting, fairly common complaint. I believe that thi > has kept modular coding from "catching on" in the midrange market. > Rather than /COPY or another program/module ("I can't see the code > unless I open another window..."), we have oodles of inline code > that's been copied from other programs. This doesn't encourage > very modular code. Agreed. If a procedure is written properly, you shouldn't need to know how it works. It works and has been well tested, and you're just employing it to get a task done. The same goes for using UPDATE instead of EXCEPT! Take the following example: (sorry about the indicator, but I'm V3R2) C kyCUSTMAS CHAIN CUSTMAS 30 C if *IN30 = *Off C eval cuOrdDate = wkOrdDate c eval cuOnOrder = cuOnOrder + wkOrdQty c update CUSTMASF c endif Assuming that you're familiar with our system... each field that starts with "cu" is part of the Customer Master file. Each field that starts with "wk" is a local "work" variable. Its very clear here that I'm updating the date ordered and the quantity on order. If instead it said: C kyCUSTMAS CHAIN CUSTMAS 30 C if *IN30 = *Off C eval cuOrdDate = wkOrdDate c eval cuOnOrder = cuOnOrder + wkOrdQty c except UpdCustmas c endif ... a few hundred lines of code ... OCUSTMAS E UpdCustmas O cuOrdDate O cuOnOrder Now I had to go down and check the output specs to make sure that both of the fields were being updated... Granted, this isnt a big deal, but its SOMETHING... What if one of the fields in the output specs has been (gasp) indicator conditioned? This is (IMHO) a bad practice, but unless I wrote the program, I don't know that its not being done! Another fun thing you can do with output specs, is indicator condition the whole record, and have multiple record updates/writes/prints that happen depending on the state of indicators in the program... talk about masking your intent! > The alternative, to use many, small and easy to understand modules i > understood to be _the_ way to go in the rest of the programming worl > It > is impractical to have an open window for each function your program > is to > perform; this is why naming conventions are so important: > GetBasePrice(ProductCode:Price) is so much easier to follow than > GETPRC(ITM:AMT) I'm still talking about "intent" here. If the > programmer > who wrote GetBasePrice did it properly, the name reflects exactly wh > the > function does - Get the base price for the item. Nothing more. Not > verify > quantities or warehouse availability. It does one thing and one thi > well. > When the maintenance programmer (me) comes along, I should not _have > to > look inside GetBasePrice to figure out what it does. The intent > should be > clear. With UPDATE, its very clear to me whats happening right there in my C specs. With EXCEPT, theres still other logic that COULD be happening... As you stated, a well written sub procedure will tell you exactly what its going to do, and you can rely on it to do that. However, an EXCEPT doesn't follow those rules. You don't know what its going to do unless you look at the code -- EXCEPT could mean many different things! Update, however, is very clear as to what its doing. Again, however, this is based on the fact that I can tell exactly what fields belong to what file just from seeing the names in the C specs, this is very different in other situations... > Back to Excpt and O specs: Yes, you have to look at the O specs to > see what > is happening, and yes, this distracts the programmer from the intent > of the > code, but I might suggest the following construct: > > C* Update if customer has tolls this month > C If STATUS = ACTIVE_G > C CallP > UpdCusYTDFrmTol(Customer:TOLYTD:TOLAMT:TOLDAT) > C EndIf > > ORMaster E UpdMasTol > O TOLYTD > O TOLAMT > O TOLDAT > > PUpdCusYTDFrmTol b > DUpdCusYTDFrmTol pi > D Cus_I 10 const > D YTD_I 15 0 const > D Amt_I 15 0 const > D Dat_I 8S 0 const > > C CusKey Klist > C Kfld Cus_I > > * Lock record > * ASSUMPTION - THIS RECORD ALREADY EXISTS!! > C CusKey Chain RMaster > C If %found > C Add YTD_I TOLYTD > C Add Amt_I TOLAMT > C Z-Add Dat_I TOLDAT > C Except UpdMasTol > C EndIf > > PUpdCusYTDFrmTol e > > With something like this, I should not have to go poke around in the > guts of UpdCusYTDFrmTol to figure out what's getting updated. I > can look at the parameters and clearly see what goes in, right at > the eval. If something is broken, the business problem (Keep track > of the last toll date and cumulative amount for quick inquiry > performance) is implemented in one function, and should be easily > repaired. > > I hope *my* intent finally came through! :-) > > Buck Calabro I agree with you here, because you put your code in a procedure, and didn't use global variables, this makes it quite clear what you're doing, and normally I wouldn't need to look at this procedure and see what its doing. However, if we're talking about using EXCEPT vs. UPDATE, (rather than the merits of modular programming) I still say that UPDATE is a better choice for much of the same reasons that modular code is a better choice. (Same reasons that I listed above...) A few notes about this particular procedure, however: 1) If the record doesn't exist, you've got a problem. Your procedure should have some sort of a success/failure return value... 2) Although you've taken care to ensure that you only update a few fields, the only fields that you COULD have updated are the ones between the "if %found" and the "except", since you just re-loaded the record in your procedure! 3) Since fields from a file are global, your CHAIN could've (potentially) changed values in another area of your program. Unfortunately, this is one of the limitations of RPG programs, you can't declare fields from a file to be local to a sub-procedure. I personally don't like my sub-procedures to change anything but the parameters being passed to them, and the return value. Of course, the easy workaround for this would be to put all of the routines that access a file into a seperate module, and only pass field values to your main program via parameters, but... we'll leave that discussion for another time :) Scott +--- | This is the RPG/400 Mailing List! | To submit a new message, send your mail to RPG400-L@midrange.com. | To subscribe to this list send email to RPG400-L-SUB@midrange.com. | To unsubscribe from this list send email to RPG400-L-UNSUB@midrange.com. | Questions should be directed to the list owner/operator: david@midrange.com +---
As an Amazon Associate we earn from qualifying purchases.
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.