(this post was pulled from my reply at my iHome at UtterAccess: thread here)
A lot of people struggle with class modules. The more prominent struggle tends to be “how do I use them” (for one of literally thousands of examples out there, check out this UA Wiki article). There’s another hurdle though, one that’s a bit less in the forefront: “when do I use them?” This post addresses the “when to use” scenarios and hopefully can give a bit of insight on what to look for as far as ways to make the best use of custom objects in the Access/VBA world.
It’s tough to say “when” is the best time to use any of the “more advanced” features that Access/VBA offers… knowing when to use them to a greater benefit comes from knowing how to use them, knowing that the tools are capable of doing XYZ, and knowing when the task is better fitted for XYZ than something else… which, if course, is difficult to know if you don’t have a lot of experience with it, so a bit of a catch 22: the more you use them, the more you find places and reasons to use them, but without using them, you really don’t know what you could do with them.
For Class modules, there’s some straightforward enough tell-tale signs that they’d be better suited. For example, when you have a module whose sole purpose is to basically do one single (probably large) task, and the coding is getting unruly (although it’s difficult to define “unruly”), the code itself may be simplified and easier maintained by going into a class module instead of a standard module.
When you have one module that crunches a lot of code to do one particular task, likely the module only really has one main entry (one public sub/function that’s called), usually with a rather long list of arguments, most of which are probably optional, set with a default. So, we have lots of “options” for this particular module, and they’re all stuffed into the main calling point of the main function of the module, and it’s really kind of ugly to look at when you call the function and see a list of 10 or 20 arguments. Something like this may be a good candidate for a Class module, because you could take all those optional settings (and even the required ones), and turn them into properties, then provide a procedure in the class to process everything that’s been previously set. So, you might go to much “cleaner” code, like this:
'in "loaded-function" form: lngResult = SomeFunction(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg11:="this", arg19="that") ' if it were in a class Dim obj As New SomeClass With obj .property1 = "blah" .property2 = "blah" .property3 = "blah" .property4 = "blah" .property5 = "blah" .property6 = "blah" .property7 = "blah" .property8 = "blah" .property11 = "blah" .property19 = "blah" lngResult = .RunMe() If obj.Errors <> "" Then Debug.Print obj.Errors End With Set obj = Nothing
A major advantage to having direct control over the properties as opposed to a long argument list, is that now you can individually handle each property assignment, such as wrapping with prepatory functions (.property1 = Trim(” SomeString “)), whereas with a long function call, if you had lots of that type of work to do, you’d have to create variables to hold temporary values, or have an even longer and uglier function call that incorporates all that stuff.
Also, with a Class module in this type of situation, the code itself (the code in the Class), tends to be a lot more organized and maintainable than code which might perform the same task were it all stuffed into a standard module. It’s hard to explain how – one of those things that comes with firsthand experience – but one small example is the built in private procedures of Initialize and Terminate of all VBA class objects. They run all the time when the class is first instantiated and when it is destroyed, and gives you a place to initialize all of those would-be optional, defaulted arguments, and also a place to clean up any loose ends on the way out, when everything’s done. Such initialization and cleanup can be done in a standard module as well, but not nearly so elegantly.
Lebans’ ConvertReportToPDF function is a good example, I think, of a standard module long-signature function that could have been more manageable in a Class module instead. See the image below:
(image broken, sorry)
Much nicer would have been a class interface for this instead:
Dim pdf As New PDFReport pdf.ReportName = "MyReport" pdf.OutputPDFFile = "C:\ThisFile.pdf" pdf.StartViewer = False pdf.FontEmbedding = True pdf.CreateReport Set pdf = Nothing
So, there’s a few cases of how class modules can make your codebase a bit more user-friendly. However, it’s fair to note here that everything mentioned above can be done with Standard Modules also, if not quite so elegantly.
Here’s a case where you want to consider thinking of a Class module, in such a scenario that it might actually make your code safer to run, unlike the above which just makes it a little easier to manage. Let’s say you have a standard module, like above, that does many things with more or less one main entry point. Let’s say it’s really a one-shot deal… you call the function, it crunches a bunch of code, then returns and you’re done, but inside that module let’s say you have lots of module level private variables that are extremely essential to the operation of the task at hand. These *really* need to be correct, or bad things will happen. Then you start thinking about your overall usage requirements for the task itself, and you ask yourself: what would ever happen if this function somehow got called again, before the original was done running… such as maybe from a timer event or from a query… it’s probably not likely, but oh boy would that really make a bad mess of things….
Enter Class modules. Because Classes are instantiated rather than Static (such as a Standard Module), you can create any number of instances of a class and never have any worries that internal variable XYZ might “cross paths” from one instance of the class to another… each instance is self-contained. This is why we can open multiple instances of a form (as explained quite nicely by Allen Browne, if you’re unfamiliar with how), and each instance can have its own data and the forms won’t get confused and accidentally pull data from this other instance instead of the one it’s supposed to.
So, here’s a case where using a class module gives a real, tangible (in the sense of programming, anyway!) plus over standard modules, rather than just making the code a little nicer to work with. This is most especially true when the Class you’re using has a fairly large scope: an instance of it might exist throughout a fairly tall stack of different procedure calls, or maybe even the entire session of the application.
Here’s another one that I made great use of… for my forms, I wanted them all to act the same way. Of course, as a novice we put code behind our forms to make standard behavior happen. As we want our applications to look more professional, we end up doing this same thing in most of our forms, and then we’ve got a lot of duplicate code sitting around in all these different forms. Somewhere around the stage of intermediate, we’ll decide to take all those duplicate codes, and put them into a standard module instead, so there’s just one copy and each form calls that copy instead of having a bunch of different versions scattered throughout the app. Good work. Now, at some point, you’ll run into a case where this function call will require a number of different parameters, and we get what we had in my first example: a huge run of arguments to call the function. Worse though, and the real kicker, is that with one static (standard module) function, you might run into some serious issues if formA and formB get tangled. At the more advanced stage, you say hey, I can make a Class module to handle all this stuff, so the code’s all defined in a single place, and then attach that class module to the form so each for has its own instance…