views:

114

answers:

2

I have an existing Perl application which is deployed to multiple customer sites. Unfortunately, the code has been cloned multiple times to customise it for individual customers. So there are now several complete copies of the code which all have slight (or major) differences.

My task is to fix this mess by creating a single, generic code base with customisations for different customers being isolated in their own libraries.

The application already has a class hierarchy (with about 120 classes) along the lines of:

Control.pm
  \__ BaseJob.pm
           \___Job1.pm
           |
           |__ Job2.pm
           |
           |__ Job3.pm

My goal is to be able to customise a specific class or method by modifying only the library for a particular customer.

My first instinct is to create sub-classes for anything that needs to be customised for a particular customer. These sub-classes would live in a customer-specific lib directory (one per customer). Then, to customise a class or method for a customer, I would just add a new sub-class to the customer library.

For example, if one method in Job2.pm needs to be customised, I might create a subclass CustomJob2 which inherits from Job2 and contains only the method to be customised.

Then in the main program, this:

Job2->some_method();

Becomes:

CustomJob2->some_method();

The problem is that this will break the code for all other customers because they don't have the CustomJob2 class in their libraries. It appears I would have to add an empty CustomJob2 class to the library for every customer.

Is there a better way?

The other possibility I've considered is to use overrides instead of sub-classes. The main program would just need a use lib to include the customer library and any methods to be customised would just be re-defined in the library. However this is probably dangerous and not considered best practice.

I seek the wisdom of StackOverflow gurus in finding the best approach to this problem.

+4  A: 

Most of your thoughts about the direction to take are solid; the problem comes in when you're calling Job2->some_method() as opposed to $job->some_method(). That is, your class method calls are only a problem because they're a bad idea in the first place -- which you're seeing because they're interfering with you leveraging OOP.

What I would do in your situation is write my code so that I use object method calls rather than class method calls, and give each project install a configuration hash that can be used to tell it what class it wants to use for a given purpose. So it'd look something like:

my $job2_class = $project->conf->{job2_class} || 'Job2';
my $job2 = new $job2_class;
$job2->some_method();
chaos
Actually most of the method calls are using objects, except the calls to 'new' which must be class method calls. But you're right that once I have an object, the problem disappears. Your idea of using a config to determine the class name is worth trying. Thanks.
noswonky
This is what I do most of the time that I need something like this.
brian d foy
+4  A: 

I'd strongly suggest that you put as much customization as possible into configuration, rather than code.

Code that looks up a value in a config object is much simpler than instantiating a subclass.

You can also mix the two by putting class names in the config file, and instantiating classes based on those names. This lets you share customizations between two or more customers more easily, as well.

Dave Rolsky
Good point. Customising by config is the ultimate goal, but the code customisations have already been done by those who came before me. So phase 1 is to isolate the custom code, phase 2 is to reduce it as much as possible and use config instead.
noswonky