A Novice Refactors
Having read commentary about coding practices, meditated, contemplated, posted a blog entry about my app and begged for constructive criticism, I found my way into a significant refactoring.
I haven't seen much about the step-by-step process of dividing and modularizing code. Here how I am approching it.
The app already uses OO, but suffers from some 260 global variables and about 6k lines of code in the main namespace.
I broke that code into about 30 different modules, testing after creating each new module, with the help of git for source control, and a couple scripts showing which files and in which subs each variable appears
Most modules still occupy the same namespace, however have access to only the minimum necessary subset of global variables. I did this using a structure like this:
[Audio_engine_setup.pm]
package main;
our ( $sampling_frequency,.... )
sub configure_freq { say "configuring soundcard at $sampling_frequency" }
All subroutines are still in the main namespace, so they work unchanged.
The 'our' declaration tells me exactly what global variables the module touches. So I can look at them and think: Do they need to be in the main namespace? Can they be lexicalized? Can they be passed as arguments?
A next step is to provide more separation by introducing a separate namespace.
package main;
our ( $sampling_frequency,....)
package Audio_engine_setup;
sub report_freq { say "soundcard is running at $sampling_frequency" }
These subroutines (and those in the main namespace they invoke) will have to be called with the full package affiliation, or be imported into the desired namespace via Exporter and 'use', or be converted to use some kind of OO.
Physically reorganizing the code provides an opportunity to partition functions, to define interfaces and to review provisional design decisions made years ago. Of course greater separation of concerns helps the software to be robust and to be readable. And having the code divided up helps anyone who wants to browse/hack.
How much separation is desirable practical is an open question. Most global variables are indices or configuration variables that are set once and read-accessed. Yes, anywhere it's possible to set them by accident. On the other hand, this is a single app, not a toolkit that will be (ab)used by other programs in an adverse environment.
For some (module set of some) applications, I never bother to separate into individual namespaces. Our developers know, for example, that by convention all Foo/Bar/*.pm are using the main namespace. But we still use separate files for organization and reducing startup overhead.
It's convenient to share a namespace, and it makes sense to use separate namespaces wherever they provide an advantage.
The dominant population of CPAN toolmakers stress isolating everything. While there's less of an imperative when writing a top-level application, it's a powerful technique.
A propos passing by parameters - the advantage of using this style is that it's easier to test stuff. This is the basis of the Dependency Injection idea.
Yes, testing is easier with parameter-passing. My app's test suite touches a few global variables, and works fine for now.
I appreciate the reference you sent on parameter-passing. It is also discussed in Higher Order Perl by MJD. He uses the example of a config file reader to show the advantages of re-entrant code based on parameter passing.
I'm moving in that direction, waiting on the analysis of which parameters are shared among subroutines.