chayashida has asked for the wisdom of the Perl Monks concerning the following question:

I have been slowly migrating old code to use Perl modules at work. I found that I was fixing the same bugs over and over in different scripts. I found it was better to fix the bug once, and then have the different scripts use those modules.

Here are my requirements:

My modules are traditional - just listing functions and exporting them for use in scripts. The modules seem to make sense - the SnapMirror functions are in the SnapMirror.pm module, the company-specific validation functions are in Company.pm, the functions relating to backups are in Backup.pm, and so on. The modules probably export more than they should, but I don't believe that to be the root of my problems.

Company.pm - functions specific to my company SnapMirrors.pm - functions specific to NetApp SnapMirrors

Basically, my code broke when I tried to make an enhancement. We wanted the is_clearcase() function to not only detect qtrees containing ClearCase data, but also to detect qtrees that are SnapMirror destinations for ClearCase data. This made is_clearcase() call is_snapmirror_destination() and get_snapmirror_source().

Searching around on the rest of the site, I see comments stating that circular dependencies are bad, but I don't have a good way of resolving this one.

I don't want to omit the error checking. It has helped us find errors in other scripts before. I also don't want to put more work on the other programmers. It's possible that every time that is_clearcase() is called, they could check for the is_snapmirror_destination() case, but it seems like this functionality belongs in the is_clearcase() function itself.

It looks like the code would work if is_clearcase() was in the SnapMirrors.pm module, but the problem is that the other coders wouldn't know to use SnapMirrors.pm when they wanted the is_clearcase() function - it doesn't seem to belong to that module.

I looked in the camel book, and they mentioned as an aside that one would use require instead of use "if you have two modules that each need a function from the other. (And we're not sure that's a good reason.)" They don't mention anything else about it.

There are other posts on this site about using require, import, and BEGIN blocks. They argue about design, but don't really provide helpful information, outside of "don't make circular dependencies."

Finally, I was messing around with a module that would use all the other ones, and then manually export functions that pointed to the correct functions. It works, but it also seems inelegant. It also seems unmaintainable.

I figure it would work if I put everything in one monolithic module, but that doesn't seem to be an elegant solution.

I seem to have coded/designed myself into a corner. Any help would be appreciated.

Thanks in advance,

Chris

Replies are listed 'Best First'.
Re: Need help with circular dependencies
by ikegami (Patriarch) on Jan 29, 2011 at 04:45 UTC
      Thank you for the link. I'm reading it now and will try it to see if it fixes my issue. Chris
Re: Need help with circular dependencies
by GrandFather (Saint) on Jan 29, 2011 at 04:54 UTC

    Why can't Company.pm 'use' SnapMirrors.pm?

    True laziness is hard work
      After reading up on the links, and trying to re-architect, I did the following:
      • Updated the code to use strict
      • Updated the code to use warnings
      • Re-ordered the use commands and @EXPORT declarations.
      When I was reading through the camel book, it mentions that the package statement, the @EXPORT statement, and the use commands need to be at the beginning of the modules, but I didn't realize that my modules didn't have them in the same order. I think when I started explicitly scoping the function calls and variables, it fixed the issues so Company.pm could use SnapMirrors.pm. In short, I want to say "thank you" to everyone. It was a lot of work, but I think I have better code now. And it's good to know that I have somewhere to turn to when I'm at wit's end. :)
      When I set it up that way, I was getting undefined subroutine errors from other modules that both Company.pm and SnapMirrors.pm use. I'm still trying to track down the errors, since they only appeared when I added the functionality described above. I'll reply with a post-mortem as this unravels. Thanks, Chris

        If you can educate your other team members in the ways of OO you will find that that can resolve many of these sorts of issue. Because you call methods on an object without needing to have it exported you can avoid import conflicts and circular module dependencies. From the perspective of someone using a module there is very little difference between a function call and a member call - just the $obj-> in front generally, and a call to new to create the object.

        If you need any help sorting out simple OO let us know. It really is much easier than you may think.

        True laziness is hard work