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

Is it bad practice to share class methods across two packages?

I have one class (ClassOne) that has some code I would like to reuse in another class (ClassTwo). The code does not need an object of ClassOne to work (it just needs some of the data that is an object of class ClassThree, which has an associated list (container) class (ClassThreeList) that holds a list of ClassThree objects). To make the example a little less abstract, ClassThree is a class that manages loading objects of its class from a SQL database.

I really only need one piece of code from ClassOne in ClassTwo, so I was thinking of just "bottling it up" in a class method in ClassOne. Then in package ClassTwo;, use ClassOne; and call that new shared method from ClassTwo as ClassOne->shared_method($data).

I realize that this introduces some coupling between ClassOne and ClassTwo, but are there any obvious reasons not to do something like this? The shared code is code that should be shared so that when it is changed for ClassOne, it also changes for ClassTwo. I.e. ClassOne and ClassTwo would use identical versions of that code, probably forever.
The example below should show the intent more clearly
package ClassOne; sub new { my ($class, %args) = @_; #... my $data = $class->load_a_bunch_of_data($arg{one}); return bless $data, $class; } use ClassThreeList; sub load_a_bunch_of_data { my ($class, $arg1) = @_; # load a bunch of data that only ClassOne cares about # ... # load a list of data that both ClassOne # and ClassTwo care about. my $item_list = ClassThreeList->load_where(objectlistid => $arg1); foreach my $item (@$item_list) { ### BEGIN CODE THAT I WANT TO SHARE ### $item->load_attr1_list; $item->load_attr2_list; $item->calculate_some_attribute. $item->etc; ### END CODE THAT I WANT TO SHARE ### } } # ClassTwo does not exist yet.
The proposed change would be to write ClassTwo and modify ClassOne:
package ClassOne; sub new { my ($class, %args) = @_; #... my $data = $class->load_a_bunch_of_data($arg{one}); return $data, $class; } use ClassThreeList; sub load_a_bunch_of_data { my ($class, $arg1) = @_; # load a bunch of data that only ClassOne cares about # ... # load a list of data that both ClassOne # and ClassTwo care about. my $item_list = ClassThreeList->load_where(objectlistid => $arg1); foreach my $item (@$item_list) { $class->shared_class_method_one( $item ); } } sub shared_class_method_one { my ($class, $item) = @_; $item->load_attr1_list; $item->load_attr2_list; $item->calculate_some_attribute. $item->etc; } package ClassTwo; use ClassOne; sub new { #... } sub new_method_that_needed_shared_code { my ($class, $arg1) = @_; my $item = ClassThree->load(objectid => $arg1); ClassOne->shared_class_method_one($item); ####### LINE IN QUESTION # +####### }

Replies are listed 'Best First'.
Re: OOP - Sharing class methods
by lachoy (Parson) on Aug 14, 2002 at 18:50 UTC

    If the code can be used by both ClassOne and ClassTwo, why not make UtilityClassOne with this (and possibly other) methods that can be used by both? Inheritance is a possibility, but this doesn't sound like an 'is a' sort of relationship and IMO inheritance gets confusing if it's used in places where composition is more appropriate (e.g., 'has a').

    Chris
    M-x auto-bs-mode

      Wow! Thank you all for the quick responses. Using a utility class, as suggested by you and perrin, seems like an intuititive way of handling this problem in the general case, especially if the shared code were to use multiple objects.

      After thinking more about the specific application, I realized that there is already "a good fit" for the utility method in ClassThree because all the shared work is just calling a handful of existing instance methods in ClassThree. The new method then becomes a shortcut instance method that calls a number of its own instance methods.
Re: OOP - Sharing class methods
by dreadpiratepeter (Priest) on Aug 14, 2002 at 18:43 UTC
    Why not make both class1 and class2 inherit from a base class which implements the common code?

    -pete
    "Pain heals. Chicks dig scars. Glory lasts forever."
      Inheritance is one way to go, but it should only be used when there is a clear inheritance relationship between the objects being modeled. A more common solution would be to just put the shared code in a separate utility class and have the other classes use that.
        How you make use of your utility class is somewhat interesting though. The naïve approach can lead to problems down the road when you make a subclass and it needs to have a slightly different version of some or all of the methods provided by the utility class.

        Here's a naïve implementation:

        package Foo; use UtilityClass; sub some_method { my $self = shift; $self->UtilityClass::some_method(@_); }
        Nothing wrong with that, is there? Well... yes, because stuff is hardcoded, you may find that, when you come to subclass foo, things will break. A better approach is:
        package Foo; use UtilityClass; sub helper_class { 'UtilityClass' }; sub some_method { goto &{ $_[0]->helper_class->can('some_method') }; }
        You could go further and add per object customization, by turning 'helper_class' into an instance method, allowing you to choose which utility class to use on an object by object basis rather than class by class...