note
davido
<p>A more real-world case that I deal with fairly regularly is one of code portability across environments that have differing constraints. Lets consider the idea of a JSON parsing class. I know this is a solved problem, but for the purposes of illustration it may be useful.</p>
<p>I may have one class of servers where it is acceptable to assume that JSON::XS is available. I may have another class of servers where a recent enough Perl version is available that I can assume JSON::PP is available. So that means 5.14 or newer. And there's yet another class of servers where I cannot make any assumptions other than core Perl, and whatever code I explicitly bundle. In those environments, I have a lot of constraints that prevent me just sucking in whatever I want from CPAN (for example, tens of thousands of servers where a goal is to keep our footprint minimal).</p>
<p>But additionally, we have these concerns: Primary: We should use the most CPU-efficient JSON parser available. Secondary: We should use the most standardized JSON parser available. Tertiary: We should use a JSON parser that has no dependencies other than what we package and ship with our code. There is a module, [mod://JSON::Any], which handles this decision for us, but may not have the same prioritization, plus it is another dependency that I'm not guaranteed to have available in all environments. So here's a home-grown strategy to select the JSON module for us, based on the order that I might prefer for this example:</p>
<c>
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
{
my @JSON_MODULES = qw(
JSON::XS
JSON
JSON::PP
JSON::Tiny
);
my $json_class;
for my $module (@JSON_MODULES) {
if (eval "require $module;") {
$json_class = $module;
last;
}
}
die "No JSON parsing class found. $0 requires one of ",
join(', ', @JSON_MODULES), ".\n"
unless $json_class;
warn "Using JSON module '$json_class'.\n";
$json_class->import(qw(encode_json decode_json));
}
my $json = '{"foo":"bar", "biff":["baz","buzz"]}';
print "\nData structure parsed from JSON:\n", Dumper(decode_json($json));
</c>
<p>If I run that on a system that has [mod://JSON::XS] installed, I get this output:</p>
<c>
Using JSON module 'JSON::XS'.
Data structure parsed from JSON:
$VAR1 = {
'foo' => 'bar',
'biff' => [
'baz',
'buzz'
]
};
</c>
<p>If I run it in an environment that has neither JSON::XS nor JSON, but is Perl 5.14 or newer, I'll get this:</p>
<c>
Using JSON module 'JSON::PP'.
</c>
<p>If I run it in an environment that has no non-core modules, and only what I bundle with the code I'm shipping, I better be sure to ship [mod://JSON::Tiny], and if I do as I should, the output would be:</p>
<c>
Using JSON module 'JSON::Tiny'.
</c>
<p>...followed by my data structure dump. The point being that now I've provided flexibility that places a low burden for what modules must be packaged or available. Sure, for many environments I can just pull something from CPAN. For some, I need to have the CPAN distribution built out as an RPM, and for some, keeping the dependencies down to only what ships with the code itself may be best.</p>
<p>This example used a set of modules that don't require an object oriented interface, but if they did, that would be just as easy; rather than calling <c>$module->import</c>, I could call <c>my $json_parser = $module->new</c>, assuming they all implemented the same OO interface.</p>
<p>If you are referring specifically to the [https://en.wikipedia.org/wiki/Factory_method_pattern|Factory pattern], the OO version I mentioned is closer to that pattern. But the Factory pattern has uses beyond only dealing with not knowing what module may be available in a given environment. The classic examples usually deal with subclasses that can't be decided at time of writing the code, but that must share a common interface with some base class, pure virtual base class, or interface role. For example, a driver needs to be able to $car->refuel, but whether the car is an instance of a Ford Van class, or a Honda Sedan class is handled by the factory.</p>
<div class="pmsig"><div class="pmsig-281137">
<br /><p>Dave</p>
</div></div>
11126634
11126634