|
Nathan/All, Observations, IMHO and my experience embedded in your reply. Paul, Your file maintenance utility has inspired me. I'm considering writing a generic component in RPG, not for maintaining, but for browsing files. I can envision exporting an api such as the following: fileReference = browseClass_Open(Library:File:Member) browseClass_SetFileReference(fileReference) browseClass_ListFromTop(recordCount) browseClass_ListNext(recordCount) browseClass_ListPrevious(recordCount) browseClass_ListFromBottom(recordCount) browseClass_ListFromKey(key:recordCount) ... browseClass_Other() ************************************************** Nathan, good stuff here. Your "browse file" component or object that your propose building is the most common user requirement that we see in application development with our clients. How many applications have required these generic requirements?: a) Show me x fields from file y in library z b) Show me 10 at a time c) For column c use "Last Name" as the column header and ... d) More advanced: Allow me to search based on field x and allow me to sort on any field e) Plus several additional generic requirements It may be a little light weight without the associated verbal description but I've documented our approach to this "#1 Most common programming task" in the following PowerPoint: http://www.planetjavainc.com/pres/AdvancedJavaDesignPatternsInWeb_files/fram e.htm (specifically in the "select and display section") Again, it is our view/goal to automate this #1 redundant requirement into a generic component that can be used regardless the specific library/file/member you are "browsing". If we can do that, we have shortened the time required to develop applications significantly. A few features from your api that may need to consider are: a) Fields to display? Often, files have 100's of fields and requirements often state show me fields (a, c, d , p, and r). Perhaps adding a parm to your "browseClass_Open(Library:file:member:field1, field 2, field 3, etc)? This allows the api user to state dynamically, what fields they want to display in their result. b) Associated with the generic browse requirement, is the need to allow the user to specify "runtime dynamic selection" to the browse. For example, if browsing an INVOICE file, the user often wants to be able to specify "Find Invoice By Number" and then they supply the invoice # at runtime. So your api may have "browseClass_setSelectionFields("INVOICE_NUM")" Obviously your design must support multiple selection fields with the array of conditions support such as "INVOICE_DATE > ? and CUSTOMER# = ? or /and ...." This is kinda like a generic QUERY/400 component with dynamic prompt. c) The other huge need is to support specific requirements based on each field being displayed. For example, if your browse file facility is showing a salary field you would want to apply current formatting or if browsing a employee department field, you may need to supply a F4 (prompt) to allow selection of a specific department. Other "field" specific characteristics include: column header, possible values (f4), formatting, authority to view, update, links or associations to other data (set lower limit and chain to an associated file). This is kinda like what DDS allows you to specify but you really need more intelligence with your fields to be generic yet powerful to a wide range of applications. d) When returning your record set from your api, you also need to know if there are more records so you can generate the "next page" feature, etc. e) actually several more generic parms to support here.... *************** </end generic "browse file" component > ********** From Nathan*************** I don't think I'd want to bite off generic add, change, and delete procedures, because such actions are generally subject to complex business rules that would need to be overridden and adapted to individual circumstance anyway. ********* end Nathan ********* <Business Rule Design for Complex individual circumstances> This is an excellent scenario to further explore OO and it's capabilities in terms of productive application development. Actually we an accommodate these complex business rules rather easily and quickly. This is nearly impossible (IMHO) to design completely generically although we can generically do most all of it. However, most applications and businesses have unique and "unpredictable" rules that apply when a) Adding a new record b) updating a record c) deleting a record, etc..... For example: assume a typical set of business rules in a HR application: a) Employees that are "MANAGERS" cannot be updated by this application, they are handled by process "Z" so DON"T ALLOW MANAGERS to be edited. b) Also, validate that any "NON-MANAGER" employees cannot be assigned a salary > 125000 " Our company's compensation policy restricts salaries to this limit. and blah blah blah... So, the requirement in this case is to allow "employee data" to be updated but restrict the updates to these conditions... The code to generically update a record can be fairly easily written and inherited for EVERY file in EVERY database and we simply allow specific complex business rules to be specified for this particular database record for "ROW" by overriding the generic insert, update, and delete methods. For example: I enhanced the Java400 demonstration application at: http://www.planetjavainc.com/wow60/runApp?id=298 to support these features: a) In the "Employees" operation only return 10 records at a time b) Don't allow editing (see edit icon to the left of each data row) if the employee is a "Manager" or his name is "Nathan" c) When updating a Row, disallow updates if the employee is not a manager and an attempt is made to set the salary > 125000 This is a small example of complex business requirement. The net is this: The general behavior of insert, update, delete is common for any relational database record; a framework should allow this default behavior but you MUST allow specific "non predictable" business rules to be enforced. This requirement was enabled basically with 2 "IF " statements and 2 minutes of development time as shown below; Everything else needed to update, insert, delete is inherited: Java class to enforce business rules: package PlanetJ.example; import PlanetJ.DataEngine.exception.*; import planetj.DataEngine.*; public class Employee extends PlanetJ.database.Row { /** * Check to see if this Row may be edited. By default, all Rows may be * edited. Subclasses may override to implement a different behavior. * * @pram ec context in which application is executing. * * @return true if Row may be edited; false otherwise. */ public boolean isEditable(ExecutingContext ec) { try { // If the employee is a Manager or Nathan (just an example) don't allow the Row to be edited if (getValueAsString("JOB").equalsIgnoreCase("MANAGER") || getValueAsString("FIRSTNME").startsWith("NATHAN")) { return false; } else { return true; } } catch (Throwable t) { t.printStackTrace(); return false; } } /** * Validate if the operation can be performed on this Row. * <p> * @param: int pOpteration Constants stored in Row ie. Row.INSERT * @param: java.security.Principal pUser * @exception DataEngineException if a this any field is invalid. */ public void validateRowOperation(int pOperation, java.security.Principal pUser) throws DataEngineException { switch (pOperation) { case (INSERT) : // Whatever you want to validate on insert case (UPDATE) : { // If the employee is a Manager they can't have a salary > 125000 if ( (! getValueAsString("JOB").equalsIgnoreCase("MANAGER")) && getValueAsDouble("SALARY") > 125000) { throw new DataEngineException("Hey they ain't worth that much! Lower that salary!"); } } case (DELETE) : // Whatever you want to validate on delete case (COPY) : // Whatever you want to validate on copy } } } ******************************** This design approach is shown here: http://www.planetjavainc.com/html/demo.html You can edit the application here: http://www.planetjavainc.com/wow60/WOWBuilder Use userid: "support@xxxxxxxxxxx" and password "support" to logon then go to step 3 setup operations. NOTE: this is on on of our development systems; it might go up and down a few times a day for software updates.... NET: We are after productivity. We now have a much more complex business application with specific rules enforced but have only spent about 15 minutes total on the application. </end> There are more similarities between ILE and OO languages than most people realize. For object instantiation in RPG: Eval myInstance = myClass_New(parameters) Callp myClass_doSomething(myInstance: parameters) Where myInstance would be a pointer to a data structure containing instance properties, which would be referenced from myClass procedure calls. For inheritance in RPG, create a new module, either importing and wrapping the procedures in a base module, or create a new module containing just procedure overrides, then bind to both modules. For polymorphism in RPG, define data exports in modules, supplementing procedure interfaces, which alter the behavior of procedures based on data content. It's true that most RPG programmers wouldn't consider following OO design patterns, unless they first immersed themselves in OO languages for a period of years. But I returned to RPG for both performance as well as productivity reasons. Nathan.
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.