|Perl Monk, Perl Meditation|
Re: Can I from within a module access variables from the calling program? (modular design)by tye (Cardinal)
|on Oct 25, 2012 at 14:47 UTC||Need Help??|
Wow. Those are some really horrible suggestions for how to solve this problem.
A well-designed component gets information from the caller by the caller passing in the information to the component. Having components try to reach up into the parent and pull stuff out of the parent is a much worse design choice than even just the using of a global variable.
You usually pass data into a module either when you 'use' the module or by calling subroutines or methods from the module. For example:
The first example leads to some subtle gotchas as well, so just use the second method (just like the designers of Log::Log4perl did):
You don't see Log::Log4perl telling you, "Just set the $log_conf global variable in your package to contain your configuration string and Log::Log4perl will go pull the value from it".
Having the child module have to know the name of the variable and forcing the parent to make that variable global are both design mistakes. Using PadWalker is particularly crazy. Can you imagine Log::Log4perl saying further "Note that if you declare the variable to be hidden from other modules by using 'my', Log::Log4perl will thwart your stated goal of hiding the variable and will dive into the guts of Perl to find the variable anyway so you might want to add a comment to make this clearer: my $log_conf # Not really private!".
Worse than the "we'll pass the information by both sides happening to use the identical name" part of this is that you also put a subtle dependency on the order in which things get done. Consider this example:
So, after you notice that this isn't working, you go to fix the order in which things are done using a more awkward layout:
But that doesn't work either.
Pass in the data needed by code when you call the code that needs the data. Then the audience playing at home has a better chance to more quickly understand what is going on.
But I also thought that Log::Log4perl dislikes being initialized more than once in the same process. So I suspect that your design has even more problems in it than just the passing of the log file name. Indeed, seeing you repeat the identical configuration for Log4perl in two places looks like the sign of poor design to me. It sounds like you noticed that you were computing the name of the log file in two places only because you didn't manage to always come up with the same name in both places.
Computing the same thing in two places using code that was copy-and-pasted is poor design. Copying and pasting code is always a bad idea (usually a really bad idea). But declaring it unacceptable for the two sides to come up with different answers is even worse.
Repeating the configuration and initialization of Log::Log4perl looks like a design mistake. Of course, I find the initialization of Log::Log4perl to be terribly awkward. The way one uses Log::Log4perl is moderately awkward, something that I'd probably consider a good choice in most modules but one that I find unfortunate for a logging module.
I believe that a logging module should go to unusual lengths to make it extra easy to add logging so that more logging is likely to get added before the need for the logging actually comes up. When a problem is found with some code and I need to debug what is going on in a deployable service, then it is often inconvenient to reproduce the problem in a way where I can use the Perl debugger. So being able to just turn up some existing logging and get a lot more details about what is going on is often a huge time saver.
So Log::Lager tries to make it really easy to add some trace. The module needs further enhancements (some of which are happening now, at least in the git repo, soon to be on CPAN, I hope). So you might also look into using it.