Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Once again, here's the idea in a nutshell:

Embed code

...

should not

...

 contain business logic.

Put another way, your app contains code that gives it unique value to your users/customers. Don't bury that code inside embed points. You can call it from embed points (you'll almost certainly have to) but don't let it live there. Good code needs a home, a place where it can be conceived, nurtured, and yes, tested and disciplined.

...

There isn't all that much difference between the two approaches so far. The procedural syntax takes a little less typing, but it's also slightly more difficult to understand. It's easier to make a mistake with eight parameters than it is with four, and . In the case of the class if you wanted to be very sure the class it was used correctly you could always create individual setter methods for each of the four initialization parameters (and report an error if not all were set before a value was requested).

...

As well, the procedural code can really only be used as procedural code. That is, you're using the class pretty much as a set of procedures, but you can also use the class instance itself. You could, if you wish, create an InvoiceHeader object that contains a list of InvoiceDetail objects, such that you could model the entire invoice in memory. It's very difficult to do anything like that with procedural code. I'll get more into the advanced uses of the InvoiceDetail class in future articles. 

It's also easier to extend the InvoiceDetail class without breaking existing functionality. As written, the CalcValues routine calculates total cost , including taxes , but not extended price excluding taxes. Adding a method to get the extended price would have zero effect on code already using the class, whereas with the procedural solution you'd have to make the additonal parameter ommitable.

...

Whichever approach you take, procedural or class, your code needs to be testable. And I don't mean the kind of testing that involves someone running an app, entering values, and making sure the results are correct. Yes, you do still need to do that, but by the time your code gets to that point you should already have a high level of confidence in the underlying logic.

The kind of testing I'm talking about is called "unit testing". The "unit" refers to the smallest testable parts of the application. In the case of the procedural version of the above code, it would involve testing a single call to the CalculateInvoiceDetailValues procedure. In the case of the class version, you would have a test for each of the methods which return a value.

That brings up another advantage of classes - it's easier to break testing down into smaller units.

The idea behind unit testing is that if all of the individual objects (or procedures) that make up your application work properly , you're much more likely to have an application that works properly as a whole.

As well, unit testing is a great way to prevent detect the introduction of new bugs as you make modifications to existing code. If you have a large suite of unit tests, and you make a change to some core code that affects how other methods or classes (or procedures) work, any bugs introduced by those changes stand a good chance of showing up in the unit tests.

...

Another key is to avoid outside dependencies in unit tests, such as a requirement to use an actual database or a user interface widget. You want your unit tests to run as quickly as possible, and with little to no chance of breakage due to something on the outside of the tested code not being set up correctly. Tests that take too long to run or are dependent on proper configuration are tests that tend not to get run. 

In the case of the CalcValues routine code, isolation is pretty easy to achieve. Although the routine code operates directly on the fields in the Detail record, this isn't a requirement. The logic can all be made internal to the class; that way the class can be used anywhere at all without modification, and the only embed code needed is whatever it takes to assign the values to and from the class. Presto! You've successfully removed the business logic from the embed point!

...

The downloadable source zip contains the modified Invoice app shown above, in C7 format.

Download the source (C8 format)