3

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.

Christophe
  • 77,139
  • 2
    Basically, you write down everything you've just said (albeit in a slightly more organized way) as documentation text; UML isn't particularly helful in this specific case. Describe what the class is for (what it does in general terms, how it fits or what its role is in your system), describe how people can make use of your class, and then describe the methods that the other devs might be interested in. Have a section on extension points: document what methods can be overridden, what are their inputs, outputs, mention any important non-obvious preconditions/postconditions that need to be met. – Filip Milovanović Jun 16 '22 at 16:35
  • 2
    Regarding the level of detail: imagine you're writing for someone who's on your team (so, a programmer who is familiar with the project), but hasn't really used your class before and doesn't really know much about it, or how to properly extend it. BTW, that person could be future you (in two weeks, or 3 months, or...), when you've forgotten what that code even was. – Filip Milovanović Jun 16 '22 at 16:38
  • 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? – Brian Karabinchak Jun 16 '22 at 18:05
  • 1
    Sounds about right. You know how, sometimes, when you're confronted with unfamiliar code, you kind of stare at it, and you pretty much understand what each line is doing, but it's not immediately obvious what the function (or the class) as a whole is trying to achieve and why - until it suddenly clicks, maybe after you've checked the surrounding/calling code, and played with the running application a bit, etc? And you're like: Oooh, that's why! Well, put that in the documentation, don't get too caught up in details. Give your readers the gist first; they can figure out the details later. – Filip Milovanović Jun 16 '22 at 20:22
  • @BrianKarabinchak Just curious about the three method (modify, validate and sanitize) - are there also other usages of those methods in the Form class? If, so they always are called in this group of 3 and in that order? – Cwt Jun 19 '22 at 16:06

3 Answers3

3

UML class diagrams answer three questions well:

  • What knows about what? (Note the lines)
  • What are these things called? (Note the class names)
  • How do you talk to these things? (Note the method signatures)

If you’re trying to explain something that isn’t the answer to those questions then use something else to explain.

Note: Abstract classes and Interfaces are different mainly in those languages that use Interface as a keyword. Typically this means they didn’t implement multiple inheritance correctly the first time around and hacked it into their language later by adding the Interface keyword.

A lower case i, “interface”, simply describes how you can talk to it. Completely ignores how it works.

candied_orange
  • 108,538
  • Ok so going off of this 1) what knows about what? (Note the lines) - this helped a lot. I think including at least a few of my subclasses would greatly improve understanding of what it's doing and why. 2) So just the class names - makes sense. 3) They don't really talk. I guess my setup is pretty simple in that it's all inheritance, all my subclasses just inherit from this Form class. Perhaps the form Class should be broken up a bit and itself be a composition of other simpler classes but for now I guess it's kind of a class that is doing too much. Appreciate the explanation of interface. – Brian Karabinchak Jun 17 '22 at 13:33
  • if you know about something, and how to talk to it, you can talk to it. You can use the lines, and the types a class knows, to show what a class (and it’s objects) can talk to. That is, “send messages” in OOP speak. In this way a class diagram can give a rough feel for how information flows through the system. It’s more than just a structure.
  • – candied_orange Jun 17 '22 at 13:57