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.
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();
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With