I am still an beginner/intermediate programmer at best so apologies if I misuse terminology.
I work with SCADA software for my job, specifically Ignition by Inductive Automation. It's made for rapid application development, and all you really need to know is that it's built upon java, so what I end up using in practice is jython 2.7.
The application I work on strictly a database CRUD application for tracking office paperwork, invoices, sales orders, etc. I realized after a while the structure of each of these forms is essentially the same set of steps, with slightly different implementations at different steps.
For creating a new invoice for instance, I get some data off the screen, I validate any values that might not be good (ex: a age that is negative) and throw an error if there is one, if not, I sanitize the data if needed (sometimes due to poor GUI design ie changing a string date to a date object), and then I run a named query with the parameters to make an insert inside of a db transaction (and running any other database queries that are related to the invoice inside the same db transaction in case something goes wrong I rollback). If everything was successful, I log a message to a history thread for the object, or if not, I log the error to a server and tell the user something went wrong.
Now some things, this abstract class (think this is the right term) Form takes care of a few things behind the scenes - it automatically figures out where the subclasses named queries should be - for invoices it will look in a folder called forms/invoices
for named queries called create
, update
, and delete
for the appropriate actions. The Form class also has a generic way of running subclasses create/update/delete statements within a the database transaction, and I keep all error parsing inside the abstract class Form so that every subclasses handles db errors or validation errors the same way.
As an example, for creating a new record, this is what my Form class looks like
def create(self, newData):
self.logger.info("running create")
# Have to reset this here in case of multiple creates, don't want a false positive on the second item
self.resetResults()
self.newData = newData
self.modifyNew()
self.validateNew()
self.sanitizeNew()
self.inputDefaultValues()
# Database transaction/error catching - but only on the top level
if not self.middleOfTransaction:
self.tryAndFailDBOperationGracefully("inserting")
else:
self._create()
# TODO - decide - if we should even update the UI if there's an error after the db stuff. Leave for now
self.updateUIAfterInsert()
Some of these things the user needs to implement if they want - modifyNew
, validateNew
, sanitizeNew
, and updateUIAfterInsert
are all pass
in the Form
class and can be skipped if not used but otherwise should be implemented if you want them to run. Other things like resetResults
, inputDefaultValues
, tryAndFailDBOperationGraceflly
, _create
are all things subclasses should not overwrite.
So how can I document something like this, what is the right way to inform someone via a UML diagram that if you want to use my Form class, here's how it works, and here's what you need to do on your end to subclass it properly? Also related - is this Form object an abstract class or is it a Interface? I am confused about the difference.
BTW, that person could be future you (in two weeks, or 3 months, or...), when you've forgotten what that code even was.
In all likelihood it will be me 6 months from now lo. I appreciate your comments. So perhaps breaking it down onto each of the main things it can do - create a record, update a record, delete a record, save a file (if there's a report associated with it), and getting email presets regarding the record (used to populate email screens). A breakdown on the functionality - and inside each of those, what the subclass is responsible for to make it work. That sound right?