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

Hi,

I need a sub which will be inherited by all classes.

BACKGROUND:
I need this because I'm trying to rewrite a huge single-file program as an OOP application. Unfortunately, practically every variable is global. As I proceed with the rewrite I need an interim solution that allows me to safely use a few global variables/methods across packages.

I found a page on CPAN that indicates I should 'use the UNIVERSAL class'. I believe they mean I should clutter up the namespace and add a method to UNIVERSAL. I'm pretty damn sure that this would result in serious bad kharma. On the other hand, it would work.

Here are my questions:
  1. What's the least ugly way to add a method/variable to UNIVERSAL?

    I could simply put...
      sub UNIVERSAL::aMethod { do something; }  
    at the top of my main script and then UNIVERSAL::aMethod will be available to everything. Is that right? Is there a better way?

  2. Is there a better way to make a universally inherited method?
  • Comment on how to make a universally inherited method?

Replies are listed 'Best First'.
Re: how to make a universally inherited method?
by lachoy (Parson) on Nov 29, 2001 at 10:12 UTC

    I agree with IlyaM, a singleton makes more sense for this. A singleton is an object that can only be instantiated once -- successive requests for a new object just return the same one.

    The Class::Singleton module probably has one of the highest POD-to-code ratios on CPAN, but it's very useful.

    Your config module:

    package My::Config; use strict; use base qw( Class::Singleton ); sub _new_instance { my ( $class ) = @_; my %data = ( var1 => 'This is var1', var2 => [ 'This', 'is', 'var2' ], ); return bless( \%data, $class ); } 1;

    And you can use it:

    my $conf = My::Config->instance; print "Config for var1 is $conf->{var1}\n";

    Easy! Good luck.

    Chris
    M-x auto-bs-mode

      Wow, this is exciting. I finally have a work-related reason to open my copy of the Gang-of-four book.

      I'll try Class::Singleton.

      Thanks
Re: how to make a universally inherited method?
by gildir (Pilgrim) on Nov 29, 2001 at 13:59 UTC
    Simple and efficient solution:

    Make one class superclass of all your othe classes. Let's call it My::Object. All your other classes will inherit from it. Place every method, that sould be 'universaly inherited' in this superclass. If you need 'global' variables, make them static variables of class My::Object. It looks like this:

    package My::Object; use vars qw($global1 $global2); sub global1 { $global1 } sub global2 { $global2 } sub globalMethod { ... } package My::SomeClass; use base qw(My::Object); package main; my $g1 = My::Object->global1; $any_my_object->globalMethod();
    Note1: It is VERY bad to mess up with 'magic' classes like UNIVERSAL is. Not because it won't work, but because it will look bad. And if it will look bad, it will be unreadable. Thik about hubris. A year after you write it you will not be able to understand how it works.

    Note2: Avoid 'global' and even 'class static' variables whenever you can. They will make your life a real pain in long-term development. Every one of them. Use of global variable of any kind can be avoided by careful object design of application. As you are rewriting your application, you should be aware of how it works, so good desing sould be your primary aim here.

Re: how to make a universally inherited method?
by IlyaM (Parson) on Nov 29, 2001 at 09:59 UTC
    sub UNIVERSAL::aMethod { do something; } works fine.

    However for your task I would use Singelton classes.

Re: how to make a universally inherited method?
by jbert (Priest) on Nov 29, 2001 at 16:57 UTC
    I'm intrigued by this (and the answers so far to your question). You have a hairy bunch of state squirreled away in a collection of global variables. OK, we all agree that is a nightmare. Do we agree that it is primarily a nightmare because it is hard to know which bits of the code touch the globals and how they interact?

    If so, how does using a Singleton class help at all? In order to keep the app working, all parts of your app will need access to the singleton. So that in itself will not help. The code is still tweaking a hairy collection of state, only now it is wrapped up in a singleton. I don't see any benefit yet.

    You want to migrate. So you plan to move data out of the singleton/global variables into managed packages/objects. Good.

    I guess that encapsulation in the singleton gives you the ability to move the data out from the singleton to a new class, as long as you change the implementation of the singleton to access the new class. But that doesn't help your code quality, since all the code which accesses the data still does so through the singleton.

    Code quality benefits only accrue once you change the code which accesses the data to do so through the new interface. At which point you will presumably remove that data from the singleton.

    But but but. The hard part is changing all the code which uses the data. AFAICS, you propose to do this twice - once to access the singleton and then once to access the 'new interface'.

    Why not just leave the old, global-accessing code in place with a view to getting it to use the new interface when it is ready (note that you can do this piecemeal, as you finish each new interface which encompasses part of your global data)? OK, you may have namespace issues (your globals will reside in main::) but they can be hacked around fairly easily by aliasing (a hopefully-temporary 'use Crud' in each new package). The namespace pollution will probably be horrible but you have that already.

    OK. I should probably go away and read up on design patterns, but this is an honest question. Can someone explain the benefits to me please?

      I suspect the better reason for making a singleton, then migrating from it is this:
      1. By forcing everything to use a singleton, you get to mark every place in the code that accesses a global.
      2. In addition, you get to regularize every access of every global, thereby reducing bugs.
      3. In addition(!), you start to find the places that were using typo'ed globals.
      Now that the accessing is regularized, you can do a global replace for a single global variable at a time, knowing that you have all your globals code still in one place.

      Even if you never go past the singleton, it's still a good idea because you have collected all your globals accessing stuff. I did that in a production project at my previous position and it worked just fine. I had one big Exporter module and each package that needed access to a given global structure would import just that one accessor symbol. I could then grep through the code and find which packages accessed which symbols. This resulted in tighter code and caught about half-a-dozen bugs in the process. :-)

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        Hi,

        exphysicist here again.

        I implemented three or four singletons today. It works well and Class::Singleton is a real gem.

        As it happens the globals break down into a number of logically grouped subsets. Each of these sets is well represented by a separate singleton. Not all methods will need to access all of the singletons so my namespace really is reduced. Plus access to the variables can be controlled and monitored.

        There's another issue to consider. As soon as I start moving methods into real classes I break the local variable glue that previously held the program together. Singletons provide an easy and quick way of handling that issue.

      The reason I proposed using a singleton is that it's simple, easy to understand and easy to port. If you have a global bunch of variables, every program needs some way to access them -- making this access easily understood seems a fairly low barrier to entry.

      You're right that a cleaner way would be to use inheritance so that any object can access the information it needs. But there seemed to me to be multiple steps between the poster's current problem and this cleaner solution.

      Chris
      M-x auto-bs-mode